// Reset stored values to the defaults.
void
CommandOptions::reset() {
+ uint8_t mac[6] = { 0x0, 0xC, 0x1, 0x2, 0x3, 0x4 };
+ double lt[2] = { 1., 1. };
+
ipversion_ = 4;
+ exchange_mode_ = DORR_SARR;
+ rate_ = 0;
+ report_delay_ = 0;
+ random_range_ = 0;
+ max_random_ = 0;
+ mac_prefix_.assign(mac, mac+6);
+ base_.resize(0);
+ num_request_.resize(0);
+ period_ = 0;
+ lost_time_set_ = 0;
+ lost_time_.assign(lt, lt+2);
+ max_drop_set_ = 0;
+ max_drop_.push_back(0);
+ max_drop_.push_back(0);
+ max_pdrop_.push_back(0.);
+ max_pdrop_.push_back(0.);
+ localname_.resize(0);
+ is_interface_ = false;
+ preload_ = 0;
+ aggressivity_ = 1;
+ local_port_ = 0;
+ seeded_ = false;
+ seed_ = 0;
+ broadcast_ = false;
+ rapid_commit_ = false;
+ use_first_ = false;
+ template_file_.resize(0);
+ xid_offset_.resize(0);
+ rnd_offset_.resize(0);
+ elp_offset_ = -1;
+ sid_offset_ = -1;
+ rip_offset_ = -1;
+ diags_.resize(0);
+ wrapped_.resize(0);
+ server_name_.resize(0);
}
-/// Parses the command-line options and records the results.
-void
-CommandOptions::parse(int argc, char* const argv[]) {
+int
+CommandOptions::parse(int argc, char** argv, bool force_reset /*=false */) {
int ch;
-
- while((ch = getopt(argc, argv, ":h")) != -1) {
+ if (force_reset) {
+ reset();
+ }
+ // Reset internal variable used by getopt to index elements
+ optind = 1;
+ while((ch = 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) {
switch (ch) {
case 'h':
- default:
usage();
+ break;
+ default:
+ ;
}
}
+ return(0);
}
void
#ifndef __COMMAND_OPTIONS_H
#define __COMMAND_OPTIONS_H
+#include <string>
+#include <vector>
+
namespace isc {
namespace perfdhcp {
///
class CommandOptions {
public:
+ enum ExchangeMode {
+ DO_SA,
+ DORR_SARR
+ };
/// \brief Default Constructor
///
///
/// \param argc Argument count passed to main().
/// \param argv Argument value array passed to main().
- void parse(int argc, char* const argv[]);
+ /// \param force_reset Force reset of state variables
+ /// return non-zero value if parse failed
+ int parse(int argc, char** argv, bool force_reset = false);
/// \brief Returns IP version
///
/// \return IP version to be used
uint8_t getIpVersion() const { return ipversion_; }
+ /// \brief Returns packet exchange mode
+ ///
+ /// \return packet exchange mode
+ ExchangeMode getExchangeMode() const { return exchange_mode_; }
+
+ /// \brief Returns echange rate
+ ///
+ /// \return exchange rate per second
+ int getRate() const { return rate_; }
+
+ /// \brief Returns report delay
+ ///
+ /// \return delay between two consecutive reports
+ int getReportDelay() const { return report_delay_; }
+
+ /// \brief Returns randomization range
+ ///
+ /// \return randomization range
+ uint32_t getRandomRange() const { return random_range_; }
+
+ /// \brief Returns MAC addr prefix
+ ///
+ /// \ return MAC addr prefix to generate different clients
+ std::vector<uint8_t> getMacPrefix() const { return mac_prefix_; }
+
+ /// \brief Returns DUID prefix
+ ///
+ /// \return DUID prefix to generate different clients
+ std::vector<uint8_t> getDuidPrefix() const { return duid_prefix_; }
+
+ /// \brief Returns base values
+ ///
+ /// \return base values for xid generation
+ std::vector<std::string> getBase() const { return base_; }
+
+ /// \brief Returns number of exchanges
+ ///
+ /// \return number of exchange requests
+ std::vector<int> getNumRequests() const { return num_request_; }
+
+ /// \brief Returns test period
+ ///
+ /// \return test period
+ int getPeriod() const { return period_; }
+
+ /// \brief Returns lost time
+ ///
+ /// \return return time before request is lost
+ std::vector<double> getLostTime() const { return lost_time_; }
+
+ /// \brief Returns max drops number
+ ///
+ /// \return maximum number of lost requests
+ std::vector<int> getMaxDrop() const { return max_drop_; }
+
+ /// \brief Returns max percentage of drops
+ ///
+ /// \return maximum percentage of lost requests
+ std::vector<double> getMaxDropPercentage() const { return max_pdrop_; }
+
+ /// \brief Returns local address or interface
+ ///
+ /// \return local address or interface
+ std::string getLocalName() const { return localname_; }
+
+ /// \brief Checks if interface name is used
+ ///
+ /// \return true if interface name was specified
+ bool isInterface() const { return is_interface_; }
+
+ /// \brief Returns number of preload exchanges
+ ///
+ /// \return number of preload exchanges
+ int getPreload() const { return preload_; }
+
+ /// \brief Returns aggressivity value
+ ///
+ /// \return aggressivity value
+ int getAggressivity() const { return aggressivity_; }
+
+ /// \brief Returns local port number
+ ///
+ /// \return local port number
+ int getLocalPort() const { return local_port_; }
+
+ /// \brief Checks if seed provided
+ ///
+ /// \return true if seed was provided
+ bool isSeeded() const { return seeded_; }
+
+ /// \brief Returns radom seed
+ ///
+ /// \return random seed
+ uint32_t getSeed() const { return seed_; }
+
+ /// \brief Checks if broadcast
+ ///
+ /// \return true if broadcast handling
+ bool isBroadcast() const { return broadcast_; }
+
+ /// \brief Check if rapid commit used
+ ///
+ /// \return true if rapid commit is used
+ bool isRapidCommit() const { return rapid_commit_; }
+
+ /// \brief Check if server-ID taken from first package
+ ///
+ /// \return true if server-iD to be taken from first package
+ bool isUseFirst() const { return use_first_; }
+
+ /// \brief Returns template file names
+ ///
+ /// \return template file names
+ std::vector<std::string> getTemplateFiles() const { return template_file_; }
+
+ /// brief Returns template offsets for xid
+ ///
+ /// \return template offsets for xid
+ std::vector<int> getXidOffset() const { return xid_offset_; }
+
+ /// \brief Returns template offsets for rnd
+ ///
+ /// \return template offsets for rnd
+ std::vector<int> getRndOffset() const { return rnd_offset_; }
+
+ /// \brief Returns template offset for elapsed time
+ ///
+ /// \return template offset for elapsed time
+ int getElpOffset() const { return elp_offset_; }
+
+ /// \brief Returns template offset for server-ID
+ ///
+ /// \return template offset for server-ID
+ int getSidOffset() const { return sid_offset_; }
+
+ /// \brief Returns template offset for requested IP
+ ///
+ /// \return template offset for requested IP
+ int getRipOffset() const { return elp_offset_; }
+
+ /// \brief Returns diagnostic selectors
+ ///
+ /// \return diagnostic selector
+ std::string getDiags() const { return diags_; }
+
+ /// \brief Returns wrapped command
+ ///
+ /// \return wrapped command
+ std::string getWrapped() const { return wrapped_; }
+
+ /// \brief Returns server name
+ ///
+ /// \return server name
+ std::string getServerName() const { return server_name_; }
+
/// \brief Print usage
///
/// Prints perfdhcp usage
void usage(void);
private:
- uint8_t ipversion_;
+ uint8_t ipversion_; ///< IP version
+ ExchangeMode exchange_mode_ ; ///< Packet exchange mode (e.g. DORR/SARR)
+ int rate_; ///< rate in exchange per second
+ int report_delay_; ///< delay between two reports
+ uint32_t random_range_; ///< randomization range
+ uint32_t max_random_; ///< maximum random value
+ std::vector<uint8_t> mac_prefix_; ///< MAC addr prefix
+ std::vector<uint8_t> duid_prefix_; ///< DUID prefix
+ std::vector<std::string> base_; ///< base values for xid
+ std::vector<int> num_request_; ///< number of exchanges
+ int period_; ///< test period
+ int lost_time_set_; ///< losttime[0] was set
+ std::vector<double> lost_time_; ///< time before a request is lost
+ int max_drop_set_; ///< max{p}drop[0] was set
+ std::vector<int> max_drop_; ///< maximum number of lost requests
+ std::vector<double> max_pdrop_; ///< maximum percentage
+ std::string localname_; ///< local address or interface
+ bool is_interface_; ///< interface vs local address
+ int preload_; ///< preload exchanges
+ int aggressivity_; ///< back to back exchanges
+ int local_port_; ///< local port number (host endian)
+ bool seeded_; ///< is a seed provided
+ uint32_t seed_; ///< randomization seed
+ bool broadcast_; ///< use broadcast
+ bool rapid_commit_; ///< add rapid commit option
+ bool use_first_; ///< where to take the server-ID
+ std::vector<std::string> template_file_; ///< template file name
+ std::vector<int> xid_offset_; ///< template offsets (xid)*/
+ std::vector<int> rnd_offset_; ///< template offsets (random)
+ int elp_offset_; ///< template offset (elapsed time)
+ int sid_offset_; ///< template offset (server ID)
+ int rip_offset_; ///< template offset (requested IP)
+ std::string diags_; ///< diagnostic selectors
+ std::string wrapped_; ///< wrapped command
+ std::string server_name_; ///< server
+
};
public virtual CommandOptions
{
public:
-
/// \brief Default Constructor
CommandOptionsTest() : CommandOptions() { }
+protected:
+
+ /// \brief Parse command line and cleanup
+ ///
+ /// Tokenizes command line to array of C-strings,
+ /// parses arguments and de-allocates C-strings
+ ///
+ /// \param s Command line to parse
+ /// \return non-zero if parsing failed
+ int process(const std::string& s) {
+ int argc = 0;
+ char** argv = tokenizeString(s, &argc);
+ int r = parse(argc, argv, true);
+ for(int i = 0; i < argc; ++i) {
+ free(argv[i]);
+ argv[i] = NULL;
+ }
+ free(argv);
+ return (r);
+ }
+
+ /// \brief Check initialized values
+ ///
+ /// Check if initialized values are correct
+ void checkDefaults() {
+ EXPECT_EQ(4, getIpVersion());
+ EXPECT_EQ(DORR_SARR, getExchangeMode());
+ EXPECT_EQ(0, getRate());
+ EXPECT_EQ(0, getReportDelay());
+ EXPECT_EQ(0, getRandomRange());
+ // EXPECT_EQ(0, max_random_);
+ // TODO MAC and DUID checks
+ EXPECT_EQ(0, getBase().size());
+ EXPECT_EQ(0, getNumRequests().size());
+ EXPECT_EQ(0, getPeriod());
+ for (int i = 0; i < getLostTime().size(); ++i) {
+ EXPECT_DOUBLE_EQ(1, getLostTime()[i]);
+ }
+ ASSERT_EQ(getMaxDrop().size(), getMaxDropPercentage().size());
+ for (int i = 0; i < getMaxDrop().size(); ++i) {
+ EXPECT_EQ(0, getMaxDrop()[i]);
+ EXPECT_EQ(0, getMaxDropPercentage()[i]);
+ }
+ EXPECT_EQ("", getLocalName());
+ EXPECT_FALSE(isInterface());
+ EXPECT_EQ(0, getPreload());
+ EXPECT_EQ(1, getAggressivity());
+ EXPECT_EQ(0, getLocalPort());
+ EXPECT_FALSE(isSeeded());
+ EXPECT_EQ(0, getSeed());
+ EXPECT_FALSE(isBroadcast());
+ EXPECT_FALSE(isRapidCommit());
+ EXPECT_FALSE(isUseFirst());
+ EXPECT_EQ(0, getTemplateFiles().size());
+ EXPECT_EQ(0, getXidOffset().size());
+ EXPECT_EQ(0, getRndOffset().size());
+ EXPECT_GT(0, getElpOffset());
+ EXPECT_GT(0, getSidOffset());
+ EXPECT_GT(0, getRipOffset());
+ EXPECT_EQ("", getDiags());
+ EXPECT_EQ("", getWrapped());
+ EXPECT_EQ("", getServerName());
+ }
+
+ /// \brief Split string to array of C-strings
+ ///
+ /// \param s String to split (tokenize)
+ /// \param num Number of tokens returned
+ /// \return array of C-strings (tokens)
+ char** tokenizeString(const std::string& s, int* num) const {
+ char** results = NULL;
+ std::stringstream ss(s);
+ std::istream_iterator<std::string> sit(ss);
+ std::istream_iterator<std::string> send;
+ std::vector<std::string> tokens(sit, send);
+
+ if (tokens.size() > 0) {
+ results = static_cast<char**>(malloc(tokens.size() * sizeof(char*)));
+ if (results == NULL) {
+ throw std::bad_alloc();
+ }
+ for (int i = 0; i < tokens.size(); ++i) {
+ char* cs = static_cast<char*>(malloc(tokens[i].length() + 1));
+ strcpy(cs, tokens[i].c_str());
+ results[i] = cs;
+ }
+ if (num != NULL) {
+ *num = tokens.size();
+ }
+ }
+
+ return results;
+ }
+
};
-TEST_F(CommandOptionsTest, defaults) {
- EXPECT_EQ(4, getIpVersion());
+TEST_F(CommandOptionsTest, Defaults) {
+ process("perfdhcp");
+ checkDefaults();
+}
+
+TEST_F(CommandOptionsTest, IpVersion) {
+ process("perfdhcp -6 -l ethx");
+ EXPECT_EQ(6, getIpVersion());
+ EXPECT_EQ("ethx", getLocalName());
}
+TEST_F(CommandOptionsTest, Rate) {
+ process("perfdhcp -4 -r 10 -l ethx");
+ EXPECT_EQ(10, getRate());
+ EXPECT_NE(0, process("perfdhcp -4 -r 0 -l ethx"));
+ EXPECT_NE(0, process("perfdhcp -6 -t 5 -l ethx"));
+ EXPECT_NE(0, process("perfdhcp -4 -n 150 -l ethx"));
+ EXPECT_NE(0, process("perfdhcp -6 -p 120 -l ethx"));
+ EXPECT_NE(0, process("perfdhcp -4 -D 1400 -l ethx"));
+}
+
+TEST_F(CommandOptionsTest, ReportDelay) {
+ process("perfdhcp -r 100 -t 17 -l ethx");
+ EXPECT_EQ(17, getReportDelay());
+}
+
+TEST_F(CommandOptionsTest, RandomRange) {
+ process("perfdhcp -l ethx");
+ EXPECT_EQ(200, getRandomRange());
+}
+
+TEST_F(CommandOptionsTest, Base) {
+ process("perfdhcp -6 -b MAC=10:20:30:40:50:60 -l ethx");
+ uint8_t mac[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60 };
+ std::vector<uint8_t> v(mac, mac+6);
+ EXPECT_EQ(v, getMacPrefix());
+ // TODO - test for DUID
+}
+
+TEST_F(CommandOptionsTest, DropTime) {
+ process("perfdhcp -l ethx -d 12");
+ ASSERT_EQ(2, getLostTime().size());
+ EXPECT_DOUBLE_EQ(12, getLostTime()[0]);
+ EXPECT_DOUBLE_EQ(1, getLostTime()[1]);
+
+ process("perfdhcp -l ethx -d 2 -d 4.7");
+ ASSERT_EQ(2, getLostTime().size());
+ EXPECT_DOUBLE_EQ(2, getLostTime()[0]);
+ EXPECT_DOUBLE_EQ(4.7, getLostTime()[1]);
+}
+
+TEST_F(CommandOptionsTest, TimeOffset) {
+ process("perfdhcp -l ethx -E 4");
+ ASSERT_EQ(5, getElpOffset());
+ ASSERT_NE(0, process("perfdhcp -l ethx -E -3"));
+ ASSERT_NE(0, process("perfdhcp -l ethx -E 3 -i"));
+}
+
+TEST_F(CommandOptionsTest, ExchangeMode) {
+ process("perfdhcp -i -l ethx");
+ ASSERT_EQ(DO_SA, getExchangeMode());
+}