Commit 4cc844f7 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac3173'

parents d8499947 57d3634c
......@@ -32,6 +32,63 @@ using namespace isc;
namespace isc {
namespace perfdhcp {
CommandOptions::LeaseType::LeaseType()
: type_(ADDRESS) {
}
CommandOptions::LeaseType::LeaseType(const Type lease_type)
: type_(lease_type) {
}
bool
CommandOptions::LeaseType::is(const Type lease_type) const {
return (lease_type == type_);
}
bool
CommandOptions::LeaseType::includes(const Type lease_type) const {
return (is(ADDRESS_AND_PREFIX) || (lease_type == type_));
}
void
CommandOptions::LeaseType::set(const Type lease_type) {
type_ = lease_type;
}
void
CommandOptions::LeaseType::fromCommandLine(const std::string& cmd_line_arg) {
if (cmd_line_arg == "address-only") {
type_ = ADDRESS;
} else if (cmd_line_arg == "prefix-only") {
type_ = PREFIX;
} else if (cmd_line_arg == "address-and-prefix") {
type_ = ADDRESS_AND_PREFIX;
} else {
isc_throw(isc::InvalidParameter, "value of lease-type: -e<lease-type>,"
" must be one of the following: 'address-only' or"
" 'prefix-only'");
}
}
std::string
CommandOptions::LeaseType::toText() const {
switch (type_) {
case ADDRESS:
return ("address-only (IA_NA option added to the client's request)");
case PREFIX:
return ("prefix-only (IA_PD option added to the client's request)");
case ADDRESS_AND_PREFIX:
return ("address-and-prefix (Both IA_NA and IA_PD options added to the"
" client's request)");
default:
isc_throw(Unexpected, "internal error: undefined lease type code when"
" returning textual representation of the lease type");
}
}
CommandOptions&
CommandOptions::instance() {
static CommandOptions options;
......@@ -52,6 +109,7 @@ CommandOptions::reset() {
// will need to reset all members many times to perform unit tests
ipversion_ = 0;
exchange_mode_ = DORA_SARR;
lease_type_.set(LeaseType::ADDRESS);
rate_ = 0;
report_delay_ = 0;
clients_num_ = 0;
......@@ -150,7 +208,7 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
// In this section we collect argument values from command line
// they will be tuned and validated elsewhere
while((opt = getopt(argc, argv, "hv46r:t:R:b:n:p:d:D:l:P:a:L:"
"s:iBc1T:X:O:E:S:I:x:w:")) != -1) {
"s:iBc1T:X:O:E:S:I:x:w:e:")) != -1) {
stream << " -" << static_cast<char>(opt);
if (optarg) {
stream << " " << optarg;
......@@ -232,6 +290,10 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
}
break;
case 'e':
initLeaseType();
break;
case 'E':
elp_offset_ = nonNegativeInteger("value of time-offset: -E<value>"
" must not be a negative integer");
......@@ -438,7 +500,7 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
// If DUID is not specified from command line we need to
// generate one.
if (duid_template_.size() == 0) {
if (duid_template_.empty()) {
generateDuidTemplate();
}
return (false);
......@@ -506,9 +568,9 @@ CommandOptions::decodeMac(const std::string& base) {
mac_template_.clear();
// Get pieces of MAC address separated with : (or even ::)
while (std::getline(s1, token, ':')) {
unsigned int ui = 0;
// Convert token to byte value using std::istringstream
if (token.length() > 0) {
unsigned int ui = 0;
try {
// Do actual conversion
ui = convertHexString(token);
......@@ -620,6 +682,12 @@ CommandOptions::validate() const {
"-6 (IPv6) must be set to use -c");
check((getExchangeMode() == DO_SA) && (getNumRequests().size() > 1),
"second -n<num-request> is not compatible with -i");
check((getIpVersion() == 4) && !getLeaseType().is(LeaseType::ADDRESS),
"-6 option must be used if lease type other than '-e address-only'"
" is specified");
check(!getTemplateFiles().empty() &&
!getLeaseType().is(LeaseType::ADDRESS),
"template files may be only used with '-e address-only'");
check((getExchangeMode() == DO_SA) && (getDropTime()[1] != 1.),
"second -d<drop-time> is not compatible with -i");
check((getExchangeMode() == DO_SA) &&
......@@ -705,6 +773,12 @@ CommandOptions::nonEmptyString(const std::string& errmsg) const {
return sarg;
}
void
CommandOptions::initLeaseType() {
std::string lease_type_arg = optarg;
lease_type_.fromCommandLine(lease_type_arg);
}
void
CommandOptions::printCommandLine() const {
std::cout << "IPv" << static_cast<int>(ipversion_) << std::endl;
......@@ -715,6 +789,7 @@ CommandOptions::printCommandLine() const {
std::cout << "SOLICIT-ADVERETISE only" << std::endl;
}
}
std::cout << "lease-type=" << getLeaseType().toText() << std::endl;
if (rate_ != 0) {
std::cout << "rate[1/s]=" << rate_ << std::endl;
}
......@@ -800,13 +875,13 @@ CommandOptions::printCommandLine() const {
void
CommandOptions::usage() const {
std::cout <<
"perfdhcp [-hv] [-4|-6] [-r<rate>] [-t<report>] [-R<range>] [-b<base>]\n"
" [-n<num-request>] [-p<test-period>] [-d<drop-time>] [-D<max-drop>]\n"
" [-l<local-addr|interface>] [-P<preload>] [-a<aggressivity>]\n"
" [-L<local-port>] [-s<seed>] [-i] [-B] [-c] [-1]\n"
" [-T<template-file>] [-X<xid-offset>] [-O<random-offset]\n"
" [-E<time-offset>] [-S<srvid-offset>] [-I<ip-offset>]\n"
" [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
"perfdhcp [-hv] [-4|-6] [-e<lease-type>] [-r<rate>] [-t<report>]\n"
" [-R<range>] [-b<base>] [-n<num-request>] [-p<test-period>]\n"
" [-d<drop-time>] [-D<max-drop>] [-l<local-addr|interface>]\n"
" [-P<preload>] [-a<aggressivity>] [-L<local-port>] [-s<seed>]\n"
" [-i] [-B] [-c] [-1] [-T<template-file>] [-X<xid-offset>]\n"
" [-O<random-offset] [-E<time-offset>] [-S<srvid-offset>]\n"
" [-I<ip-offset>] [-x<diagnostic-selector>] [-w<wrapped>] [server]\n"
"\n"
"The [server] argument is the name/address of the DHCP server to\n"
"contact. For DHCPv4 operation, exchanges are initiated by\n"
......@@ -838,6 +913,14 @@ CommandOptions::usage() const {
"-d<drop-time>: Specify the time after which a requeqst is treated as\n"
" having been lost. The value is given in seconds and may contain a\n"
" fractional component. The default is 1 second.\n"
"-e<lease-type>: A type of lease being requested from the server. It\n"
" may be one of the following: address-only, prefix-only or\n"
" address-and-prefix. The address-only indicates that the regular\n"
" address (v4 or v6) will be requested. The prefix-only indicates\n"
" that the IPv6 prefix will be requested. The address-and-prefix\n"
" indicates that both IPv6 address and prefix will be requested.\n"
" The '-e prefix-only' and -'e address-and-prefix' must not be\n"
" used with -4.\n"
"-E<time-offset>: Offset of the (DHCPv4) secs field / (DHCPv6)\n"
" elapsed-time option in the (second/request) template.\n"
" The value 0 disables it.\n"
......
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
......@@ -30,6 +31,79 @@ namespace perfdhcp {
///
class CommandOptions : public boost::noncopyable {
public:
/// \brief A class encapsulating the type of lease being requested from the
/// server.
///
/// This class comprises convenience functions to convert the lease type
/// to the textual format and to match the appropriate lease type with the
/// value of the -e<lease-type> parameter specified from the command line.
class LeaseType {
public:
/// The lease type code.
enum Type {
ADDRESS,
PREFIX,
ADDRESS_AND_PREFIX
};
LeaseType();
/// \brief Constructor from lease type code.
///
/// \param lease_type A lease type code.
LeaseType(const Type lease_type);
/// \brief Checks if lease type has the specified code.
///
/// \param lease_type A lease type code to be checked.
///
/// \return true if lease type is matched with the specified code.
bool is(const Type lease_type) const;
/// \brief Checks if lease type implies request for the address,
/// prefix (or both) as specified by the function argument.
///
/// This is a convenience function to check that, for the lease type
/// specified from the command line, the address or prefix
/// (IA_NA or IA_PD) option should be sent to the server.
/// For example, if user specified '-e address-and-prefix' in the
/// command line this function will return true for both ADDRESS
/// and PREFIX, because both address and prefix is requested from
/// the server.
///
/// \param lease_type A lease type.
///
/// \return true if the lease type implies creation of the address,
/// prefix or both as specified by the argument.
bool includes(const Type lease_type) const;
/// \brief Sets the lease type code.
///
/// \param lease_type A lease type code.
void set(const Type lease_type);
/// \brief Sets the lease type from the command line argument.
///
/// \param cmd_line_arg An argument specified in the command line
/// as -e<lease-type>:
/// - address-only
/// - prefix-only
///
/// \throw isc::InvalidParameter if the specified argument is invalid.
void fromCommandLine(const std::string& cmd_line_arg);
/// \brief Return textual representation of the lease type.
///
/// \return A textual representation of the lease type.
std::string toText() const;
private:
Type type_; ///< A lease type code.
};
/// 2-way (cmd line param -i) or 4-way exchanges
enum ExchangeMode {
DO_SA,
......@@ -71,6 +145,11 @@ public:
/// \return packet exchange mode.
ExchangeMode getExchangeMode() const { return exchange_mode_; }
/// \ brief Returns the type of lease being requested.
///
/// \return type of lease being requested by perfdhcp.
LeaseType getLeaseType() const { return (lease_type_); }
/// \brief Returns echange rate.
///
/// \return exchange rate per second.
......@@ -300,6 +379,11 @@ private:
/// \throw InvalidParameter if string is empty.
std::string nonEmptyString(const std::string& errmsg) const;
/// \brief Decodes the lease type requested by perfdhcp from optarg.
///
/// \throw InvalidParameter if lease type value specified is invalid.
void initLeaseType();
/// \brief Set number of clients.
///
/// Interprets the getopt() "opt" global variable as the number of clients
......@@ -373,6 +457,8 @@ private:
uint8_t ipversion_;
/// Packet exchange mode (e.g. DORA/SARR)
ExchangeMode exchange_mode_;
/// Lease Type to be obtained: address only, IPv6 prefix only.
LeaseType lease_type_;
/// Rate in exchange per second
int rate_;
/// Delay between generation of two consecutive
......@@ -396,7 +482,7 @@ private:
/// Indicates number of -d<value> parameters specified by user.
/// If this value goes above 2, command line parsing fails.
uint8_t drop_time_set_;
/// Time to elapse before request is lost. The fisrt value of
/// Time to elapse before request is lost. The first value of
/// two-element vector refers to DO/SA exchanges,
/// second value refers to RA/RR. Default values are { 1, 1 }
std::vector<double> drop_time_;
......@@ -433,12 +519,12 @@ private:
/// Indicates that we take server id from first received packet.
bool use_first_;
/// Packet template file names. These files store template packets
/// that are used for initiating echanges. Template packets
/// that are used for initiating exchanges. Template packets
/// read from files are later tuned with variable data.
std::vector<std::string> template_file_;
/// Offset of transaction id in template files. First vector
/// element points to offset for DISCOVER/SOLICIT messages,
/// second element points to trasaction id offset for
/// second element points to transaction id offset for
/// REQUEST messages
std::vector<int> xid_offset_;
/// Random value offset in templates. Random value offset
......
......@@ -260,6 +260,7 @@ public:
/// assumed dropped. Negative value disables it.
/// \param archive_enabled if true packets archive mode is enabled.
/// In this mode all packets are stored throughout the test execution.
/// \param boot_time Holds the timestamp when perfdhcp has been started.
ExchangeStats(const ExchangeType xchg_type,
const double drop_time,
const bool archive_enabled,
......@@ -1183,7 +1184,7 @@ public:
/// \throw isc::InvalidOperation if no exchange type added to
/// track statistics.
void printStats() const {
if (exchanges_.size() == 0) {
if (exchanges_.empty()) {
isc_throw(isc::InvalidOperation,
"no exchange type added for tracking");
}
......@@ -1238,7 +1239,7 @@ public:
/// \throw isc::InvalidOperation if no exchange type added to
/// track statistics or packets archive mode is disabled.
void printTimestamps() const {
if (exchanges_.size() == 0) {
if (exchanges_.empty()) {
isc_throw(isc::InvalidOperation,
"no exchange type added for tracking");
}
......@@ -1261,7 +1262,7 @@ public:
///
/// \throw isc::InvalidOperation if no custom counters added for tracking.
void printCustomCounters() const {
if (custom_counters_.size() == 0) {
if (custom_counters_.empty()) {
isc_throw(isc::InvalidOperation, "no custom counters specified");
}
for (CustomCountersMapIterator it = custom_counters_.begin();
......
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -97,6 +97,36 @@ TestControl::TestControl() {
reset();
}
void
TestControl::copyIaOptions(const Pkt6Ptr& pkt_from, Pkt6Ptr& pkt_to) {
if (!pkt_from || !pkt_to) {
isc_throw(BadValue, "NULL pointers must not be specified as arguments"
" for the copyIaOptions function");
}
// IA_NA
if (CommandOptions::instance().getLeaseType()
.includes(CommandOptions::LeaseType::ADDRESS)) {
OptionPtr option = pkt_from->getOption(D6O_IA_NA);
if (!option) {
isc_throw(OptionNotFound, "IA_NA option not found in the"
" server's response");
}
pkt_to->addOption(option);
}
// IA_PD
if (CommandOptions::instance().getLeaseType()
.includes(CommandOptions::LeaseType::PREFIX)) {
OptionPtr option = pkt_from->getOption(D6O_IA_PD);
if (!option) {
isc_throw(OptionNotFound, "IA_PD option not found in the"
" server's response");
}
pkt_to->addOption(option);
}
}
std::string
TestControl::byte2Hex(const uint8_t b) const {
const int b1 = b / 16;
......@@ -289,6 +319,22 @@ TestControl::factoryIana6(Option::Universe, uint16_t,
return (OptionPtr(new Option(Option::V6, D6O_IA_NA, buf_ia_na)));
}
OptionPtr
TestControl::factoryIapd6(Option::Universe, uint16_t,
const OptionBuffer& buf) {
// @todo allow different values of T1, T2 and IAID.
static const uint8_t buf_array[] = {
0, 0, 0, 1, // IAID = 1
0, 0, 3600 >> 8, 3600 & 0xff, // T1 = 3600
0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400
};
OptionBuffer buf_ia_pd(buf_array, buf_array + sizeof(buf_array));
// Append sub-options to IA_PD.
buf_ia_pd.insert(buf_ia_pd.end(), buf.begin(), buf.end());
return (OptionPtr(new Option(Option::V6, D6O_IA_PD, buf_ia_pd)));
}
OptionPtr
TestControl::factoryRapidCommit6(Option::Universe, uint16_t,
const OptionBuffer&) {
......@@ -412,11 +458,11 @@ TestControl::getElapsedTime(const T& pkt1, const T& pkt2) {
uint64_t
TestControl::getNextExchangesNum() const {
CommandOptions& options = CommandOptions::instance();
// Reset number of exchanges.
uint64_t due_exchanges = 0;
// Get current time.
ptime now(microsec_clock::universal_time());
if (now >= send_due_) {
// Reset number of exchanges.
uint64_t due_exchanges = 0;
// If rate is specified from the command line we have to
// synchornize with it.
if (options.getRate() != 0) {
......@@ -594,15 +640,15 @@ TestControl::openSocket() const {
uint16_t port = options.getLocalPort();
int sock = 0;
uint8_t family = (options.getIpVersion() == 6) ? AF_INET6 : AF_INET;
uint8_t family = (options.getIpVersion() == 6) ? AF_INET6 : AF_INET;
IOAddress remoteaddr(servername);
// Check for mismatch between IP option and server address
if (family != remoteaddr.getFamily()) {
isc_throw(InvalidParameter,
"Values for IP version: " <<
isc_throw(InvalidParameter,
"Values for IP version: " <<
static_cast<unsigned int>(options.getIpVersion()) <<
" and server address: " << servername << " are mismatched.");
" and server address: " << servername << " are mismatched.");
}
if (port == 0) {
......@@ -691,7 +737,7 @@ TestControl::sendPackets(const TestControlSocket& socket,
if (options.getIpVersion() == 4) {
// No template packets means that no -T option was specified.
// We have to build packets ourselfs.
if (template_buffers_.size() == 0) {
if (template_buffers_.empty()) {
sendDiscover4(socket, preload);
} else {
// @todo add defines for packet type index that can be
......@@ -701,7 +747,7 @@ TestControl::sendPackets(const TestControlSocket& socket,
} else {
// No template packets means that no -T option was specified.
// We have to build packets ourselfs.
if (template_buffers_.size() == 0) {
if (template_buffers_.empty()) {
sendSolicit6(socket, preload);
} else {
// @todo add defines for packet type index that can be
......@@ -920,7 +966,7 @@ TestControl::readPacketTemplate(const std::string& file_name) {
// Expect even number of digits.
if (hex_digits.size() % 2 != 0) {
isc_throw(OutOfRange, "odd number of digits in template file");
} else if (hex_digits.size() == 0) {
} else if (hex_digits.empty()) {
isc_throw(OutOfRange, "template file " << file_name << " is empty");
}
std::vector<uint8_t> binary_stream;
......@@ -1079,6 +1125,11 @@ TestControl::registerOptionFactories6() const {
D6O_IA_NA,
&TestControl::factoryIana6);
// D6O_IA_PD option factory.
LibDHCP::OptionFactoryRegister(Option::V6,
D6O_IA_PD,
&TestControl::factoryIapd6);
}
factories_registered = true;
......@@ -1577,13 +1628,13 @@ TestControl::sendRequest6(const TestControlSocket& socket,
}
pkt6->addOption(opt_serverid);
}
// Set IA_NA option.
OptionPtr opt_ia_na = advertise_pkt6->getOption(D6O_IA_NA);
if (!opt_ia_na) {
isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received "
"packet");
}
pkt6->addOption(opt_ia_na);
// Copy IA_NA or IA_PD option from the Advertise message to the Request
// message being sent to the server. This will throw exception if the
// option to be copied is not found. Note that this function will copy
// one of IA_NA or IA_PD options, depending on the lease-type value
// specified in the command line.
copyIaOptions(advertise_pkt6, pkt6);
// Set default packet data.
setDefaults6(socket, pkt6);
......@@ -1732,7 +1783,20 @@ TestControl::sendSolicit6(const TestControlSocket& socket,
}
pkt6->addOption(Option::factory(Option::V6, D6O_CLIENTID, duid));
pkt6->addOption(Option::factory(Option::V6, D6O_ORO));
pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA));
// Depending on the lease-type option specified, we should request
// IPv6 address (with IA_NA) or IPv6 prefix (IA_PD) or both.
// IA_NA
if (CommandOptions::instance().getLeaseType()
.includes(CommandOptions::LeaseType::ADDRESS)) {
pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA));
}
// IA_PD
if (CommandOptions::instance().getLeaseType()
.includes(CommandOptions::LeaseType::PREFIX)) {
pkt6->addOption(Option::factory(Option::V6, D6O_IA_PD));
}
setDefaults6(socket, pkt6);
pkt6->pack();
......
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2012-2013 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
......@@ -55,6 +55,13 @@ static const size_t DHCPV6_SERVERID_OFFSET = 22;
/// Default DHCPV6 IA_NA offset in the packet template.
static const size_t DHCPV6_IA_NA_OFFSET = 40;
/// @brief Exception thrown when the required option is not found in a packet.
class OptionNotFound : public Exception {
public:
OptionNotFound(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) { };
};
/// \brief Test Control class.
///
/// This singleton class is used to run the performance test with
......@@ -160,7 +167,7 @@ public:
/// \param socket socket descriptor.
TestControlSocket(const int socket);
/// \brief Destriuctor of the socket wrapper class.
/// \brief Destructor of the socket wrapper class.
///
/// Destructor closes wrapped socket.
~TestControlSocket();
......@@ -197,7 +204,7 @@ public:
/// The default generator pointer.
typedef boost::shared_ptr<NumberGenerator> NumberGeneratorPtr;
/// \brief Sequential numbers generatorc class.
/// \brief Sequential numbers generator class.
class SequentialGenerator : public NumberGenerator {
public:
/// \brief Constructor.
......@@ -213,7 +220,7 @@ public:
}
}
/// \brief Generate number sequentialy.
/// \brief Generate number sequentially.
///
/// \return generated number.
virtual uint32_t generate() {
......@@ -241,7 +248,7 @@ public:
/// brief\ Run performance test.
///
/// Method runs whole performance test. Command line options must
/// be parsed prior to running this function. Othewise function will
/// be parsed prior to running this function. Otherwise function will
/// throw exception.
///
/// \throw isc::InvalidOperation if command line options are not parsed.
......@@ -280,7 +287,7 @@ protected:
/// only via \ref instance method.
TestControl();
/// \brief Check if test exit condtitions fulfilled.
/// \brief Check if test exit conditions fulfilled.
///
/// Method checks if the test exit conditions are fulfilled.
/// Exit conditions are checked periodically from the
......@@ -338,6 +345,17 @@ protected:
uint16_t type,
const dhcp::OptionBuffer& buf);
/// \brief Factory function to create IA_PD option.
///
/// this factory function creates DHCPv6 IA_PD option instance.
///
/// \param u universe (ignored).
/// \param type option-type (ignored).
/// \param buf option-buffer carrying sub-options.
static dhcp::OptionPtr factoryIapd6(dhcp::Option::Universe u,
uint16_t type,
const dhcp::OptionBuffer& buf);
/// \brief Factory function to create DHCPv6 ORO option.
///
/// This factory function creates DHCPv6 Option Request Option instance.
......@@ -371,7 +389,7 @@ protected:
/// \brief Factory function to create DHCPv4 Request List option.
///
/// This factory function creayes DHCPv4 PARAMETER_REQUEST_LIST option
/// This factory function creates DHCPv4 PARAMETER_REQUEST_LIST option
/// instance with the following set of requested options:
/// - DHO_SUBNET_MASK,
/// - DHO_BROADCAST_ADDRESS,
......@@ -527,7 +545,7 @@ protected:
/// \brief Process received DHCPv6 packet.
///
/// Method performs processing of the received DHCPv6 packet,
/// updates statistics and responsds to the server if required,