#include <exceptions/exceptions.h>
#include <dhcp/iface_mgr.h>
#include <dhcp/duid.h>
+#include <dhcp/option.h>
#include <cfgrpt/config_report.h>
+#include <util/encode/hex.h>
#include <boost/lexical_cast.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace std;
using namespace isc;
+using namespace isc::dhcp;
namespace isc {
namespace perfdhcp {
server_name_.clear();
v6_relay_encapsulation_level_ = 0;
generateDuidTemplate();
+ extra_opts_.clear();
}
bool
// In this section we collect argument values from command line
// they will be tuned and validated elsewhere
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:w:e:f:F:")) != -1) {
+ "s:iBc1T:X:O:o:E:S:I:x:W:w:e:f:F:")) != -1) {
stream << " -" << static_cast<char>(opt);
if (optarg) {
stream << " " << optarg;
" -O<value> must be greater than 3 ");
rnd_offset_.push_back(offset_arg);
break;
+ case 'o': {
+
+ // we must know how to contruct the option: whether it's v4 or v6.
+ check( (ipversion_ != 4) && (ipversion_ != 6),
+ "-4 or -6 must be explicitly specified before -o is used.");
+
+ // custom option (expected format: code,hexstring)
+ std::string opt_text = std::string(optarg);
+ size_t coma_loc = opt_text.find(',');
+ check(coma_loc == std::string::npos,
+ "-o option must provide option code, a coma and hexstring for"
+ " the option content, e.g. -o60,646f63736973 for sending option"
+ " 60 (class-id) with the value 'docsis'");
+ int code = 0;
+
+ // Try to parse the option code
+ try {
+ code = boost::lexical_cast<int>(opt_text.substr(0,coma_loc));
+ check(code <= 0, "Option code can't be negative");
+ } catch (boost::bad_lexical_cast&) {
+ isc_throw(InvalidParameter, "Invalid option code specified for "
+ "-o option, expected format: -o<integer>,<hexstring>");
+ }
+
+ // Now try to interpret the hexstring
+ opt_text = opt_text.substr(coma_loc + 1);
+ std::vector<uint8_t> bin;
+ try {
+ isc::util::encode::decodeHex(opt_text, bin);
+ } catch (BadValue& e) {
+ isc_throw(InvalidParameter, "Error during encoding option -o:"
+ << e.what());
+ }
+ // Create and remember the option.
+ OptionPtr opt(new Option(ipversion_ == 4 ? Option::V4 : Option::V6,
+ code, bin));
+ extra_opts_.insert(make_pair(code, opt));
+ break;
+ }
case 'p':
period_ = positiveInteger("value of test period:"
" -p<value> must be a positive integer");
}
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");
- }
+ std::string line;
+ std::ifstream infile(mac_list_file_.c_str());
+ size_t cnt = 0;
+ while (std::getline(infile, line)) {
+ cnt++;
+ stringstream tmp;
+ tmp << "invalid mac in input line " << cnt;
+ // Let's print more meaningful error that contains line with error.
+ check(decodeMacString(line), tmp.str());
+ }
}
bool CommandOptions::decodeMacString(const std::string& line) {
#include <boost/noncopyable.hpp>
+#include <dhcp/option.h>
#include <stdint.h>
#include <string>
#include <vector>
/// \return wrapped command (start/stop).
std::string getWrapped() const { return wrapped_; }
+ /// @brief Returns extra options to be inserted.
+ ///
+ /// @return container with options
+ const isc::dhcp::OptionCollection& getExtraOpts() const { return extra_opts_; }
+
/// \brief Returns server name.
///
/// \return server name.
/// Indicates how many DHCPv6 relay agents are simulated.
uint8_t v6_relay_encapsulation_level_;
+
+ /// @brief Extra options to be sent in each packet.
+ isc::dhcp::OptionCollection extra_opts_;
};
} // namespace perfdhcp
<arg choice="opt" rep="norepeat"><option>-M <replaceable class="parameter">mac-list-file</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-n <replaceable class="parameter">num-request</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-O <replaceable class="parameter">random-offset</replaceable></option></arg>
+ <arg choice="opt" rep="norepeat"><option>-o <replaceable class="parameter">code,hexstring</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-p <replaceable class="parameter">test-period</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-P <replaceable class="parameter">preload</replaceable></option></arg>
<arg choice="opt" rep="norepeat"><option>-r <replaceable class="parameter">rate</replaceable></option></arg>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>-o <replaceable class="parameter">code,hexstring</replaceable></option></term>
+ <listitem>
+ <para>
+ This forces perfdhcp to insert specified extra option (or
+ options if used several times) into packets being
+ transmitted. The code specifies option code and the
+ hexstring is a hexadecimal string that defines the content
+ of the option. Care should be taken as perfdhcp does not
+ offer any kind of logic behind those options. They're
+ simply inserted into packets being sent as is. Be careful
+ not to insert options that are already inserted. For
+ example, to insert client class identifier (option code
+ 60) with a string 'docsis', you can use -o
+ 60,646f63736973. The <option>-o</option> may be used
+ multiple times. It is necessary to specify protocol family
+ (either <option>-4</option> or <option>-6</option>) before
+ using <option>-o</option>.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-P <replaceable class="parameter">preload</replaceable></option></term>
<listitem>
// Set client identifier
pkt4->addOption(generateClientId(pkt4->getHWAddr()));
+ // Add any extra options that user may have specified.
+ addExtraOpts(pkt4);
+
pkt4->pack();
IfaceMgr::instance().send(pkt4);
if (!preload) {
// Create message of the specified type.
Pkt4Ptr msg = createRequestFromAck(ack);
setDefaults4(socket, msg);
+
+ // Add any extra options that user may have specified.
+ addExtraOpts(msg);
+
msg->pack();
// And send it.
IfaceMgr::instance().send(msg);
// Prepare the message of the specified type.
Pkt6Ptr msg = createMessageFromReply(msg_type, reply);
setDefaults6(socket, msg);
+
+ // Add any extra options that user may have specified.
+ addExtraOpts(msg);
+
msg->pack();
// And send it.
IfaceMgr::instance().send(msg);
// and local (relay) address.
setDefaults4(socket, pkt4);
+ // Add any extra options that user may have specified.
+ addExtraOpts(pkt4);
+
// Set hardware address
pkt4->setHWAddr(offer_pkt4->getHWAddr());
// Set client id.
pkt4->addOption(opt_requested_ip);
setDefaults4(socket, boost::static_pointer_cast<Pkt4>(pkt4));
+
+ // Add any extra options that user may have specified.
+ addExtraOpts(pkt4);
+
// Prepare on-wire data.
pkt4->rawPack();
IfaceMgr::instance().send(boost::static_pointer_cast<Pkt4>(pkt4));
// Set default packet data.
setDefaults6(socket, pkt6);
+
+ // Add any extra options that user may have specified.
+ addExtraOpts(pkt6);
+
// Prepare on-wire data.
pkt6->pack();
IfaceMgr::instance().send(pkt6);
pkt6->addOption(opt_clientid);
// Set default packet data.
setDefaults6(socket, pkt6);
+
+ // Add any extra options that user may have specified.
+ addExtraOpts(pkt6);
+
// Prepare on wire data.
pkt6->rawPack();
// Send packet.
}
setDefaults6(socket, pkt6);
+
+ // Add any extra options that user may have specified.
+ addExtraOpts(pkt6);
+
pkt6->pack();
IfaceMgr::instance().send(pkt6);
if (!preload) {
// Prepare on-wire data.
pkt6->rawPack();
setDefaults6(socket, pkt6);
+
+ // Add any extra options that user may have specified.
+ addExtraOpts(pkt6);
+
// Send solicit packet.
IfaceMgr::instance().send(pkt6);
if (!preload) {
}
}
+void
+TestControl::addExtraOpts(const Pkt4Ptr& pkt) {
+ // All all extra options that the user may have specified
+ CommandOptions& options = CommandOptions::instance();
+ const dhcp::OptionCollection& extra_opts = options.getExtraOpts();
+ for (auto entry : extra_opts) {
+ pkt->addOption(entry.second);
+ }
+}
+
+void
+TestControl::addExtraOpts(const Pkt6Ptr& pkt) {
+ // All all extra options that the user may have specified
+ CommandOptions& options = CommandOptions::instance();
+ const dhcp::OptionCollection& extra_opts = options.getExtraOpts();
+ for (auto entry : extra_opts) {
+ pkt->addOption(entry.second);
+ }
+}
+
bool
TestControl::testDiags(const char diag) const {
std::string diags(CommandOptions::instance().getDiags());
void setDefaults6(const TestControlSocket& socket,
const dhcp::Pkt6Ptr& pkt);
+ /// @brief Inserts extra options specified by user.
+ ///
+ /// Note: addExtraOpts for v4 and v6 could easily be turned into a template.
+ /// However, this would require putting code here that uses CommandOptions,
+ /// and that would create dependency between test_control.h and
+ /// command_options.h.
+ ///
+ /// @param pkt4 options will be added here
+ void addExtraOpts(const dhcp::Pkt4Ptr& pkt4);
+
+ /// @brief Inserts extra options specified by user.
+ ///
+ /// Note: addExtraOpts for v4 and v6 could easily be turned into a template.
+ /// However, this would require putting code here that uses CommandOptions,
+ /// and that would create dependency between test_control.h and
+ /// command_options.h.
+ ///
+ /// @param pkt6 options will be added here
+ void addExtraOpts(const dhcp::Pkt6Ptr& pkt6);
+
/// \brief Find if diagnostic flag has been set.
///
/// \param diag diagnostic flag (a,e,i,s,r,t,T).