|
@@ -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_file_list_.clear();
|
|
|
+ mac_list_.clear();
|
|
|
num_request_.clear();
|
|
|
period_ = 0;
|
|
|
drop_time_set_ = 0;
|
|
@@ -133,6 +136,7 @@ CommandOptions::reset() {
|
|
|
broadcast_ = false;
|
|
|
rapid_commit_ = false;
|
|
|
use_first_ = false;
|
|
|
+ use_relayed_v6_ = false;
|
|
|
template_file_.clear();
|
|
|
rnd_offset_.clear();
|
|
|
xid_offset_.clear();
|
|
@@ -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;
|
|
|
|
|
|
+ // act as a relay Agent (single char option and R/r are taken already).
|
|
|
+ case 'A':
|
|
|
+ use_relayed_v6_ = true;
|
|
|
+ // TODO: actually use level, at the moment we support only 1 level.
|
|
|
+ // See comment in https://github.com/isc-projects/kea/pull/22#issuecomment-243405600
|
|
|
+ int level;
|
|
|
+ level = positiveInteger(
|
|
|
+ " -A<encapusulation_levels> must be a positive integer");
|
|
|
+ if (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_file_list_ = 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_file_list_.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_file_list_.empty()) {
|
|
|
+ std::cout << "mac-file-list=" << mac_file_list_ << 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_levels>] [-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,10 @@ 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-file-list>: A text file containing a list of macs, one per line.\n"
|
|
|
+ " If provided a random mac will be choosen for every exchange.\n"
|
|
|
+ " Must not be used in conjunction with the -b parameter.\n"
|
|
|
+ " In the v6 case MAC addresses are used to generate DUID-LLs.\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 +1085,8 @@ 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_levels>: When acting in DHCPv6 mode, send out relay packets.\n"
|
|
|
+ " <encapusulation_levels> specifies how many relays forwarded this message\n"
|
|
|
"\n"
|
|
|
"The remaining options are used only in conjunction with -r:\n"
|
|
|
"\n"
|