Commit c5b4a598 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'github22'

parents 37a2fed8 d27d5ffe
......@@ -96,6 +96,9 @@ We have received the following contributions:
- Angelo Failla, Facebook
2016-04: Fixes for transaction id generation in perfdhcp
2016-08: Using a file as a source of MAC addresses to be used
in new transactions.
2016-08: Support for generating relayed DHCPv6 traffic.
- Razvan Becheriu, Qualitance
2016-05: Added support for Cassandra
......
......@@ -1094,12 +1094,12 @@ elif test "${log4cplus_path}" != "yes" ; then
LOG4CPLUS_LIBS="-L${log4cplus_path}/lib"
else
# If not specified, try some common paths.
log4cplusdirs="/usr/local /usr/pkg /opt /opt/local"
log4cplusdirs="/usr /usr/local /usr/pkg /opt /opt/local"
for d in $log4cplusdirs
do
if test -f $d/include/log4cplus/logger.h; then
LOG4CPLUS_INCLUDES="-I$d/include"
LOG4CPLUS_LIBS="-L$d/lib"
LOG4CPLUS_LIBS="-L$d/lib -L$d/lib64"
break
fi
done
......
......@@ -20,6 +20,7 @@
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fstream>
using namespace std;
......@@ -117,6 +118,8 @@ CommandOptions::reset() {
mac_template_.assign(mac, mac + 6);
duid_template_.clear();
base_.clear();
mac_list_file_.clear();
mac_list_.clear();
num_request_.clear();
period_ = 0;
drop_time_set_ = 0;
......@@ -142,6 +145,7 @@ CommandOptions::reset() {
diags_.clear();
wrapped_.clear();
server_name_.clear();
v6_relay_encapsulation_level_ = 0;
generateDuidTemplate();
}
......@@ -205,10 +209,11 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
std::ostringstream stream;
stream << "perfdhcp";
int num_mac_list_files = 0;
// 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:"
while((opt = getopt(argc, argv, "hv46A:r:t:R:b:n:p:d:D:l:P:a:L:M:"
"s:iBc1T:X:O:E:S:I:x:w:e:f:F:")) != -1) {
stream << " -" << static_cast<char>(opt);
if (optarg) {
......@@ -219,6 +224,19 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
use_first_ = true;
break;
// Simulate DHCPv6 relayed traffic.
case 'A':
// @todo: At the moment we only support simulating a single relay
// agent. In the future we should extend it to up to 32.
// See comment in https://github.com/isc-projects/kea/pull/22#issuecomment-243405600
v6_relay_encapsulation_level_ =
static_cast<uint8_t>(positiveInteger("-A<encapusulation-level> must"
" be a positive integer"));
if (v6_relay_encapsulation_level_ != 1) {
isc_throw(isc::InvalidParameter, "-A only supports 1 at the moment.");
}
break;
case '4':
check(ipversion_ == 6, "IP version already set to 6");
ipversion_ = 4;
......@@ -340,6 +358,13 @@ CommandOptions::initialize(int argc, char** argv, bool print_cmd_line) {
boost::lexical_cast<std::string>(std::numeric_limits<uint16_t>::max()));
break;
case 'M':
check(num_mac_list_files >= 1, "only -M option can be specified");
num_mac_list_files++;
mac_list_file_ = std::string(optarg);
loadMacs();
break;
case 'n':
num_req = positiveInteger("value of num-request:"
" -n<value> must be a positive integer");
......@@ -554,7 +579,7 @@ CommandOptions::decodeBase(const std::string& base) {
// Currently we only support mac and duid
if ((b.substr(0, 4) == "mac=") || (b.substr(0, 6) == "ether=")) {
decodeMac(b);
decodeMacBase(b);
} else if (b.substr(0, 5) == "duid=") {
decodeDuid(b);
} else {
......@@ -565,7 +590,7 @@ CommandOptions::decodeBase(const std::string& base) {
}
void
CommandOptions::decodeMac(const std::string& base) {
CommandOptions::decodeMacBase(const std::string& base) {
// Strip string from mac=
size_t found = base.find('=');
static const char* errmsg = "expected -b<base> format for"
......@@ -685,12 +710,45 @@ CommandOptions::convertHexString(const std::string& text) const {
return ui;
}
void CommandOptions::loadMacs() {
std::string line;
std::ifstream infile(mac_list_file_.c_str());
while (std::getline(infile, line)) {
check(decodeMacString(line), "invalid mac in input");
}
}
bool CommandOptions::decodeMacString(const std::string& line) {
// decode mac string into a vector of uint8_t returns true in case of error.
std::istringstream s(line);
std::string token;
std::vector<uint8_t> mac;
while(std::getline(s, token, ':')) {
// Convert token to byte value using std::istringstream
if (token.length() > 0) {
unsigned int ui = 0;
try {
// Do actual conversion
ui = convertHexString(token);
} catch (isc::InvalidParameter&) {
return (true);
}
// If conversion succeeded store byte value
mac.push_back(ui);
}
}
mac_list_.push_back(mac);
return (false);
}
void
CommandOptions::validate() const {
check((getIpVersion() != 4) && (isBroadcast() != 0),
"-B is not compatible with IPv6 (-6)");
check((getIpVersion() != 6) && (isRapidCommit() != 0),
"-6 (IPv6) must be set to use -c");
check(getIpVersion() == 4 && isUseRelayedV6(),
"Can't use -4 with -A, it's a V6 only option.");
check((getIpVersion() != 6) && (getReleaseRate() != 0),
"-F<release-rate> may be used with -6 (IPv6) only");
check((getExchangeMode() == DO_SA) && (getNumRequests().size() > 1),
......@@ -757,7 +815,8 @@ CommandOptions::validate() const {
check((getTemplateFiles().size() < 2) && (getRequestedIpOffset() >= 0),
"second/request -T<template-file> must be set to "
"use -I<ip-offset>");
check((!getMacListFile().empty() && base_.size() > 0),
"Can't use -b with -M option");
}
void
......@@ -871,6 +930,9 @@ CommandOptions::printCommandLine() const {
if (use_first_) {
std::cout << "use-first" << std::endl;
}
if (!mac_list_file_.empty()) {
std::cout << "mac-list-file=" << mac_list_file_ << std::endl;
}
for (size_t i = 0; i < template_file_.size(); ++i) {
std::cout << "template-file[" << i << "]=" << template_file_[i] << std::endl;
}
......@@ -910,15 +972,16 @@ CommandOptions::printCommandLine() const {
void
CommandOptions::usage() const {
std::cout <<
"perfdhcp [-hv] [-4|-6] [-e<lease-type>] [-r<rate>] [-f<renew-rate>]\n"
"perfdhcp [-hv] [-4|-6] [-A<encapusulation-level>] [-e<lease-type>]"
" [-r<rate>] [-f<renew-rate>]\n"
" [-F<release-rate>] [-t<report>] [-R<range>] [-b<base>]\n"
" [-n<num-request>] [-p<test-period>] [-d<drop-time>]\n"
" [-D<max-drop>] [-l<local-addr|interface>] [-P<preload>]\n"
" [-a<aggressivity>] [-L<local-port>] [-s<seed>] [-i] [-B]\n"
" [-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>]\n"
" [server]\n"
" [-c] [-1] [-M<mac-list-file>] [-T<template-file>]\n"
" [-X<xid-offset>] [-O<random-offset] [-E<time-offset>]\n"
" [-S<srvid-offset>] [-I<ip-offset>] [-x<diagnostic-selector>]\n"
" [-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"
......@@ -979,6 +1042,11 @@ CommandOptions::usage() const {
" via which exchanges are initiated.\n"
"-L<local-port>: Specify the local port to use\n"
" (the value 0 means to use the default).\n"
"-M<mac-list-file>: A text file containing a list of MAC addresses,\n"
" one per line. If provided, a MAC address will be choosen randomly\n"
" from this list for every new exchange. In the DHCPv6 case, MAC\n"
" addresses are used to generate DUID-LLs. This parameter must not be\n"
" used in conjunction with the -b parameter.\n"
"-O<random-offset>: Offset of the last octet to randomize in the template.\n"
"-P<preload>: Initiate first <preload> exchanges back to back at startup.\n"
"-r<rate>: Initiate <rate> DORA/SARR (or if -i is given, DO/SA)\n"
......@@ -1018,6 +1086,12 @@ CommandOptions::usage() const {
" the exchange rate (given by -r<rate>). Furthermore the sum of\n"
" this value and the renew-rate (given by -f<rate) must be equal\n"
" to or less than the exchange rate.\n"
"-A<encapusulation-level>: Specifies that relayed traffic must be\n"
" generated. The argument specifies the level of encapsulation, i.e.\n"
" how many relay agents are simulated. Currently the only supported\n"
" <encapsulation-level> value is 1, which means that the generated\n"
" traffic is an equivalent of the traffic passing through a single\n"
" relay agent.\n"
"\n"
"The remaining options are used only in conjunction with -r:\n"
"\n"
......
......@@ -24,6 +24,9 @@ namespace perfdhcp {
class CommandOptions : public boost::noncopyable {
public:
/// @brief A vector holding MAC addresses.
typedef std::vector<std::vector<uint8_t> > MacAddrsVector;
/// \brief A class encapsulating the type of lease being requested from the
/// server.
///
......@@ -269,11 +272,31 @@ public:
/// \return true if server-iD to be taken from first package.
bool isUseFirst() const { return use_first_; }
/// \brief Check if generated DHCPv6 messages shuold appear as relayed.
///
/// \return true if generated traffic should appear as relayed.
bool isUseRelayedV6() const { return (v6_relay_encapsulation_level_ > 0); }
/// \brief Returns template file names.
///
/// \return template file names.
std::vector<std::string> getTemplateFiles() const { return template_file_; }
/// \brief Returns location of the file containing list of MAC addresses.
///
/// MAC addresses read from the file are used by the perfdhcp in message
/// exchanges with the DHCP server.
///
/// \return Location of the file containing list of MAC addresses.
std::string getMacListFile() const { return mac_list_file_; }
/// \brief Returns reference to a vector of MAC addresses read from a file.
///
/// Every MAC address is represented as a vector.
///
/// \return Reference to a vector of vectors.
const MacAddrsVector& getMacsFromFile() const { return mac_list_; }
/// brief Returns template offsets for xid.
///
/// \return template offsets for xid.
......@@ -427,7 +450,7 @@ private:
///
/// \param base Base string given as -b mac=00:01:02:03:04:05.
/// \throws isc::InvalidParameter if mac address is invalid.
void decodeMac(const std::string& base);
void decodeMacBase(const std::string& base);
/// \brief Decodes base DUID provided with -b<base>.
///
......@@ -454,6 +477,14 @@ private:
/// \throw isc::InvalidParameter if string does not represent hex byte.
uint8_t convertHexString(const std::string& hex_text) const;
/// \brief Opens the text file containing list of macs (one per line)
/// and adds them to the mac_list_ vector.
void loadMacs();
/// \brief Decodes a mac string into a vector of uint8_t and adds it to the
/// mac_list_ vector.
bool decodeMacString(const std::string& line);
/// IP protocol version to be used, expected values are:
/// 4 for IPv4 and 6 for IPv6, default value 0 means "not set"
uint8_t ipversion_;
......@@ -528,6 +559,14 @@ private:
/// that are used for initiating exchanges. Template packets
/// read from files are later tuned with variable data.
std::vector<std::string> template_file_;
/// Location of a file containing a list of MAC addresses, one per line.
/// This can be used if you don't want to generate MAC address from a
/// base MAC address, but rather provide the file with a list of MAC
/// addresses to be randomly picked. Note that in DHCPv6 those MAC
/// addresses will be used to generate DUID-LL.
std::string mac_list_file_;
/// List of MAC addresses loaded from a file.
std::vector<std::vector<uint8_t> > mac_list_;
/// Offset of transaction id in template files. First vector
/// element points to offset for DISCOVER/SOLICIT messages,
/// second element points to transaction id offset for
......@@ -551,6 +590,8 @@ private:
std::string wrapped_;
/// Server name specified as last argument of command line.
std::string server_name_;
/// Indicates how many DHCPv6 relay agents are simulated.
uint8_t v6_relay_encapsulation_level_;
};
} // namespace perfdhcp
......
......@@ -11,7 +11,7 @@
<refentry>
<refentryinfo>
<date>February 19, 2014</date>
<date>August 31, 2016</date>
</refentryinfo>
<refmeta>
......@@ -27,7 +27,7 @@
<docinfo>
<copyright>
<year>2014</year>
<year>2016</year>
<holder>Internet Systems Consortium, Inc. ("ISC")</holder>
</copyright>
</docinfo>
......@@ -37,6 +37,7 @@
<command>perfdhcp</command>
<arg><option>-1</option></arg>
<arg><option>-4|-6</option></arg>
<arg><option>-A <replaceable class="parameter">encapsulation-level</replaceable></option></arg>
<arg><option>-a <replaceable class="parameter">aggressivity</replaceable></option></arg>
<arg><option>-b <replaceable class="parameter">base</replaceable></option></arg>
<arg><option>-B</option></arg>
......@@ -52,6 +53,7 @@
<arg><option>-I <replaceable class="parameter">ip-offset</replaceable></option></arg>
<arg><option>-l <replaceable class="parameter">local-address|interface</replaceable></option></arg>
<arg><option>-L <replaceable class="parameter">local-port</replaceable></option></arg>
<arg><option>-M <replaceable class="parameter">mac-list-file</replaceable></option></arg>
<arg><option>-n <replaceable class="parameter">num-request</replaceable></option></arg>
<arg><option>-O <replaceable class="parameter">random-offset</replaceable></option></arg>
<arg><option>-p <replaceable class="parameter">test-period</replaceable></option></arg>
......@@ -392,6 +394,19 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>-M <replaceable class="parameter">mac-list-file</replaceable></option></term>
<listitem>
<para>
A text file containing a list of MAC addresses,
one per line. If provided, a MAC address will be choosen randomly
from this list for every new exchange. In the DHCPv6 case, MAC
addresses are used to generate DUID-LLs. This parameter must not be
used in conjunction with the -b parameter.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-P <replaceable class="parameter">preload</replaceable></option></term>
<listitem>
......@@ -594,6 +609,20 @@
</listitem>
</varlistentry>
<varlistentry>
<term><option>-A <replaceable class="parameter">encapsulation-level</replaceable></option></term>
<listitem>
<para>
Specifies that relayed traffic must be
generated. The argument specifies the level of encapsulation, i.e.
how many relay agents are simulated. Currently the only supported
<replaceable class="parameter">encapsulation-level</replaceable>
value is 1, which means that the generated traffic is an equivalent
of the traffic passing through a single relay agent.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect2>
......
......@@ -21,6 +21,7 @@
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/foreach.hpp>
#include <algorithm>
#include <fstream>
#include <stdio.h>
#include <stdlib.h>
......@@ -79,8 +80,9 @@ TestControl::instance() {
return (test_control);
}
TestControl::TestControl() {
reset();
TestControl::TestControl()
: number_generator_(0, CommandOptions::instance().getMacsFromFile().size()) {
reset();
}
void
......@@ -464,38 +466,51 @@ TestControl::factoryRequestList4(Option::Universe u,
}
std::vector<uint8_t>
TestControl::generateMacAddress(uint8_t& randomized) const {
TestControl::generateMacAddress(uint8_t& randomized) {
CommandOptions& options = CommandOptions::instance();
uint32_t clients_num = options.getClientsNum();
if (clients_num < 2) {
return (options.getMacTemplate());
}
// Get the base MAC address. We are going to randomize part of it.
std::vector<uint8_t> mac_addr(options.getMacTemplate());
if (mac_addr.size() != HW_ETHER_LEN) {
isc_throw(BadValue, "invalid MAC address template specified");
}
uint32_t r = macaddr_gen_->generate();
randomized = 0;
// Randomize MAC address octets.
for (std::vector<uint8_t>::iterator it = mac_addr.end() - 1;
it >= mac_addr.begin();
--it) {
// Add the random value to the current octet.
(*it) += r;
++randomized;
if (r < 256) {
// If we are here it means that there is no sense
// to randomize the remaining octets of MAC address
// because the following bytes of random value
// are zero and it will have no effect.
break;
}
// Randomize the next octet with the following
// byte of random value.
r >>= 8;
const CommandOptions::MacAddrsVector& macs = options.getMacsFromFile();
// if we are using the -M option return a random one from the list...
if (macs.size() > 0) {
uint16_t r = number_generator_();
if (r >= macs.size()) {
r = 0;
}
return macs[r];
} else {
// ... otherwise use the standard behavior
uint32_t clients_num = options.getClientsNum();
if (clients_num < 2) {
return (options.getMacTemplate());
}
// Get the base MAC address. We are going to randomize part of it.
std::vector<uint8_t> mac_addr(options.getMacTemplate());
if (mac_addr.size() != HW_ETHER_LEN) {
isc_throw(BadValue, "invalid MAC address template specified");
}
uint32_t r = macaddr_gen_->generate();
randomized = 0;
// Randomize MAC address octets.
for (std::vector<uint8_t>::iterator it = mac_addr.end() - 1;
it >= mac_addr.begin();
--it) {
// Add the random value to the current octet.
(*it) += r;
++randomized;
if (r < 256) {
// If we are here it means that there is no sense
// to randomize the remaining octets of MAC address
// because the following bytes of random value
// are zero and it will have no effect.
break;
}
// Randomize the next octet with the following
// byte of random value.
r >>= 8;
}
return (mac_addr);
}
return (mac_addr);
}
OptionPtr
......@@ -508,20 +523,50 @@ TestControl::generateClientId(const dhcp::HWAddrPtr& hwaddr) const {
}
std::vector<uint8_t>
TestControl::generateDuid(uint8_t& randomized) const {
TestControl::generateDuid(uint8_t& randomized) {
CommandOptions& options = CommandOptions::instance();
uint32_t clients_num = options.getClientsNum();
if ((clients_num == 0) || (clients_num == 1)) {
return (options.getDuidTemplate());
}
// Get the base DUID. We are going to randomize part of it.
std::vector<uint8_t> duid(options.getDuidTemplate());
// @todo: add support for DUIDs of different sizes.
std::vector<uint8_t> mac_addr(generateMacAddress(randomized));
duid.resize(duid.size());
std::copy(mac_addr.begin(), mac_addr.end(),
duid.begin() + duid.size() - mac_addr.size());
return (duid);
const CommandOptions::MacAddrsVector& macs = options.getMacsFromFile();
// pick a random mac address if we are using option -M..
if (macs.size() > 0) {
uint16_t r = number_generator_();
if (r >= macs.size()) {
r = 0;
}
std::vector<uint8_t> mac = macs[r];
// DUID_LL is in this format
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | 3 | hardware type (16 bits) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// . .
// . link-layer address (variable length) .
// . .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// No C++11 so initializer list support, building a vector<uint8_t> is a
// pain...
uint8_t duid_ll[] = {0, 3, 0, 1, 0, 0, 0, 0, 0, 0};
// copy duid_ll array into the vector
std::vector<uint8_t> duid(duid_ll,
duid_ll + sizeof(duid_ll) / sizeof(duid_ll[0]));
// put the mac address bytes at the end
std::copy(mac.begin(), mac.end(), duid.begin() + 4);
return (duid);
} else {
uint32_t clients_num = options.getClientsNum();
if ((clients_num == 0) || (clients_num == 1)) {
return (options.getDuidTemplate());
}
// Get the base DUID. We are going to randomize part of it.
std::vector<uint8_t> duid(options.getDuidTemplate());
// @todo: add support for DUIDs of different sizes.
duid.resize(duid.size());
std::copy(mac_addr.begin(), mac_addr.end(),
duid.begin() + duid.size() - mac_addr.size());
return (duid);
}
}
uint32_t
......@@ -745,7 +790,12 @@ TestControl::openSocket() const {
if (port == 0) {
if (family == AF_INET6) {
// need server port (547) because the server is acting as a relay agent
port = DHCP6_CLIENT_PORT;
// if acting as a relay agent change port.
if (options.isUseRelayedV6()) {
port = DHCP6_SERVER_PORT;
}
} else if (options.getIpVersion() == 4) {
port = 67; // TODO: find out why port 68 is wrong here.
}
......@@ -2192,6 +2242,18 @@ TestControl::setDefaults6(const TestControlSocket& socket,
pkt->setLocalAddr(socket.addr_);
// The remote server's name or IP.
pkt->setRemoteAddr(IOAddress(options.getServerName()));
// only act as a relay agent when told so.
// TODO: support more level of encapsulation, at the moment we only support
// one, via -A1 option.
if (options.isUseRelayedV6()) {
Pkt6::RelayInfo relay_info;
relay_info.msg_type_ = DHCPV6_RELAY_FORW;
relay_info.hop_count_ = 1;
relay_info.linkaddr_ = IOAddress(socket.addr_);
relay_info.peeraddr_ = IOAddress(socket.addr_);
pkt->addRelayInfo(relay_info);
}
}
bool
......
......@@ -15,6 +15,7 @@
#include <dhcp/dhcp6.h>
#include <dhcp/pkt4.h>
#include <dhcp/pkt6.h>
#include <util/random/random_number_generator.h>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
......@@ -281,6 +282,9 @@ protected:
/// only via \ref instance method.
TestControl();
/// Generate uniformly distributed integers in range of [min, max]
isc::util::random::UniformRandomIntegerGenerator number_generator_;
/// \brief Check if test exit conditions fulfilled.
///
/// Method checks if the test exit conditions are fulfilled.
......@@ -465,7 +469,7 @@ protected:
/// is ignored).
/// \throw isc::BadValue if \ref generateMacAddress throws.
/// \return vector representing DUID.
std::vector<uint8_t> generateDuid(uint8_t& randomized) const;
std::vector<uint8_t> generateDuid(uint8_t& randomized);
/// \brief Generate MAC address.
///
......@@ -481,7 +485,7 @@ protected:
/// \throw isc::BadValue if MAC address template (default or specified
/// from the command line) has invalid size (expected 6 octets).
/// \return generated MAC address.
std::vector<uint8_t> generateMacAddress(uint8_t& randomized) const;
std::vector<uint8_t> generateMacAddress(uint8_t& randomized);
/// \brief generate transaction id.
///
......
......@@ -7,9 +7,10 @@
#include <config.h>
#include <cstddef>
#include <fstream>
#include <gtest/gtest.h>