// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // Copyright (c) 2001-2007 International Computer Science Institute // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software") // to deal in the Software without restriction, subject to the conditions // listed in the XORP LICENSE file. These conditions include: you must // preserve this copyright notice, and you cannot mention the copyright // holders in advertising related to the Software without their permission. // The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This // notice is a summary of the XORP LICENSE file; the license in that file is // legally binding. #ident "$XORP: xorp/rip/test_request.cc,v 1.26 2007/02/16 22:47:16 pavlin Exp $" #include #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.XORP"; 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() { 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(), 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; }