/// stored on the list of sent packets. When packets are matched the
/// round trip time can be calculated.
///
-/// \tparam T class representing DHCPv4 or DHCPv6 packet.
-template <class T>
+/// \param T class representing DHCPv4 or DHCPv6 packet.
+template <class T = dhcp::Pkt4>
class StatsMgr : public boost::noncopyable {
public:
/// \param packet packet which transaction id is to be hashed.
/// \throw isc::BadValue if packet is null.
/// \return transaction id hash.
- static uint32_t hashTransid(const boost::shared_ptr<const T>& packet) {
+ static uint32_t hashTransid(const boost::shared_ptr<T>& packet) {
if (!packet) {
isc_throw(BadValue, "Packet is null");
}
/// }
/// \endcode
typedef boost::multi_index_container<
- boost::shared_ptr<const T>,
+ boost::shared_ptr<T>,
boost::multi_index::indexed_by<
boost::multi_index::sequenced<>,
boost::multi_index::hashed_non_unique<
boost::multi_index::global_fun<
- const boost::shared_ptr<const T>&,
+ const boost::shared_ptr<T>&,
uint32_t,
&ExchangeStats::hashTransid
>
> PktList;
/// Packet list iterator for sequencial access to elements.
- typedef typename PktList::const_iterator PktListIterator;
+ typedef typename PktList::iterator PktListIterator;
/// Packet list index to search packets using transaction id hash.
typedef typename PktList::template nth_index<1>::type
PktListTransidHashIndex;
///
/// \param packet packet object to be added.
/// \throw isc::BadValue if packet is null.
- void appendSent(const boost::shared_ptr<const T>& packet) {
+ void appendSent(const boost::shared_ptr<T>& packet) {
if (!packet) {
isc_throw(BadValue, "Packet is null");
}
///
/// \param packet packet object to be added.
/// \throw isc::BadValue if packet is null.
- void appendRcvd(const boost::shared_ptr<const T>& packet) {
+ void appendRcvd(const boost::shared_ptr<T>& packet) {
if (!packet) {
isc_throw(BadValue, "Packet is null");
}
/// \param rcvd_packet received packet
/// \throw isc::BadValue if sent or received packet is null.
/// \throw isc::Unexpected if failed to calculate timestamps
- void updateDelays(const boost::shared_ptr<const T>& sent_packet,
- const boost::shared_ptr<const T>& rcvd_packet) {
+ void updateDelays(const boost::shared_ptr<T>& sent_packet,
+ const boost::shared_ptr<T>& rcvd_packet) {
if (!sent_packet) {
isc_throw(BadValue, "Sent packet is null");
}
/// \throw isc::BadValue if received packet is null.
/// \return packet having specified transaction or NULL if packet
/// not found
- boost::shared_ptr<const T> matchPackets(const boost::shared_ptr<const T>& rcvd_packet) {
+ boost::shared_ptr<T>
+ matchPackets(const boost::shared_ptr<T>& rcvd_packet) {
if (!rcvd_packet) {
isc_throw(BadValue, "Received packet is null");
}
// that the received packet we got has no corresponding
// sent packet so orphans counter has to be updated.
++orphans_;
- return(boost::shared_ptr<const T>());
+ return(boost::shared_ptr<T>());
} else if (next_sent_ == sent_packets_.end()) {
// Even if there are still many unmatched packets on the
// list we might hit the end of it because of unordered
// If we are here, it means that both ordered lookup and
// unordered lookup failed. Searched packet is not on the list.
++orphans_;
- return(boost::shared_ptr<const T>());
+ return(boost::shared_ptr<T>());
}
// Packet is matched so we count it. We don't count unmatched packets
// as they are counted as orphans with a separate counter.
++rcvd_packets_num_;
- boost::shared_ptr<const T> sent_packet(*next_sent_);
+ boost::shared_ptr<T> sent_packet(*next_sent_);
// If packet was found, we assume it will be never searched
// again. We want to delete this packet from the list to
// improve performance of future searches.
/// \return number of received packets.
uint64_t getRcvdPacketsNum() const { return(rcvd_packets_num_); }
+ /// \brief Return number of dropped packets.
+ ///
+ /// Method returns number of dropped packets.
+ ///
+ /// \return number of dropped packets.
+ uint64_t getDroppedPacketsNum() const {
+ uint64_t drops = 0;
+ if (getSentPacketsNum() > getRcvdPacketsNum()) {
+ drops = getSentPacketsNum() - getRcvdPacketsNum();
+ }
+ return(drops);
+ }
+
/// \brief Print main statistics for packet exchange.
///
/// Method prints main statistics for particular exchange.
/// number of dropped packets and number of orphans.
void printMainStats() const {
using namespace std;
- uint64_t drops = 0;
- if (getRcvdPacketsNum() >= getSentPacketsNum()) {
- drops = getRcvdPacketsNum() - getSentPacketsNum();
- }
cout << "sent packets: " << getSentPacketsNum() << endl
<< "received packets: " << getRcvdPacketsNum() << endl
- << "drops: " << drops << endl
+ << "drops: " << getDroppedPacketsNum() << endl
<< "orphans: " << getOrphans() << endl;
}
for (PktListIterator it = rcvd_packets_.begin();
it != rcvd_packets_.end();
++it) {
- boost::shared_ptr<const T> rcvd_packet = *it;
+ boost::shared_ptr<T> rcvd_packet = *it;
PktListTransidHashIndex& idx =
archived_packets_.template get<1>();
std::pair<PktListTransidHashIterator,
++it) {
if ((*it_archived)->getTransid() ==
rcvd_packet->getTransid()) {
- boost::shared_ptr<const T> sent_packet = *it_archived;
+ boost::shared_ptr<T> sent_packet = *it_archived;
// Get sent and received packet times.
ptime sent_time = sent_packet->getTimestamp();
ptime rcvd_time = rcvd_packet->getTimestamp();
/// \throw isc::BadValue if invalid exchange type specified or
/// packet is null.
void passSentPacket(const ExchangeType xchg_type,
- const boost::shared_ptr<const T>& packet) {
+ const boost::shared_ptr<T>& packet) {
ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
xchg_stats->appendSent(packet);
}
/// or packet is null.
/// \throw isc::Unexpected if corresponding packet was not
/// found on the list of sent packets.
- void passRcvdPacket(const ExchangeType xchg_type,
- const boost::shared_ptr<const T>& packet) {
+ boost::shared_ptr<T>
+ passRcvdPacket(const ExchangeType xchg_type,
+ const boost::shared_ptr<T>& packet) {
ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
- boost::shared_ptr<const T> sent_packet
+ boost::shared_ptr<T> sent_packet
= xchg_stats->matchPackets(packet);
if (sent_packet) {
xchg_stats->appendRcvd(packet);
}
}
+ return(sent_packet);
}
/// \brief Return minumum delay between sent and received packet.
return(xchg_stats->getRcvdPacketsNum());
}
+ /// \brief Return total number of dropped packets.
+ ///
+ /// Method returns total number of dropped packets for specified
+ /// exchange type.
+ ///
+ /// \param xchg_type exchange type.
+ /// \throw isc::BadValue if invalid exchange type specified.
+ /// \return number of dropped packets.
+ uint64_t getDroppedPacketsNum(const ExchangeType xchg_type) const {
+ ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
+ return(xchg_stats->getDroppedPacketsNum());
+ }
+
/// \brief Return name of the exchange.
///
/// Method returns name of the specified exchange type.
TestControl::TestControl() :
send_due_(microsec_clock::universal_time()),
- last_sent_(send_due_) {
+ last_sent_(send_due_),
+ transid_gen_(new TransidGenerator()) {
}
bool
TestControl::checkExitConditions() const {
CommandOptions& options = CommandOptions::instance();
- if ((options.getNumRequests().size() > 0) &&
- (sent_packets_0_ >= options.getNumRequests()[0])) {
- return(true);
- } else if ((options.getNumRequests().size() == 2) &&
- (sent_packets_1_ >= options.getNumRequests()[1])) {
- return(true);
+ // Check if we reached maximum number of DISCOVER/SOLICIT sent.
+ if (options.getNumRequests().size() > 0) {
+ if (options.getIpVersion() == 4) {
+ if (getSentPacketsNum(StatsMgr4::XCHG_DO) >=
+ options.getNumRequests()[0]) {
+ return(true);
+ }
+ } else if (options.getIpVersion() == 6) {
+ if (stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA) >=
+ options.getNumRequests()[0]) {
+ return(true);
+ }
+ }
+ }
+ // Check if we reached maximum number REQUEST packets.
+ if (options.getNumRequests().size() == 2) {
+ if (options.getIpVersion() == 4) {
+ if (stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) >=
+ options.getNumRequests()[1]) {
+ return(true);
+ }
+ } else if (options.getIpVersion() == 6) {
+ if (stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR) >=
+ options.getNumRequests()[1]) {
+ return(true);
+ }
+ }
+ }
+ // Check if we reached maximum number of drops of OFFER/ADVERTISE packets.
+ if (options.getMaxDrop().size() > 0) {
+ if (options.getIpVersion() == 4) {
+ if (stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_DO) >=
+ options.getMaxDrop()[0]) {
+ return(true);
+ }
+ } else if (options.getIpVersion() == 6) {
+ if (stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_SA) >=
+ options.getMaxDrop()[0]) {
+ return(true);
+ }
+ }
+ }
+ // Check if we reached maximum number of drops of ACK/REPLY packets.
+ if (options.getMaxDrop().size() == 2) {
+ if (options.getIpVersion() == 4) {
+ if (stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) >=
+ options.getMaxDrop()[1]) {
+ return(true);
+ }
+ } else if (options.getIpVersion() == 6) {
+ if (stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_RR) >=
+ options.getMaxDrop()[1]) {
+ return(true);
+ }
+ }
+ }
+ // Check if we reached maximum drops percentage of OFFER/ADVERTISE packets.
+ if (options.getMaxDropPercentage().size() > 0) {
+ if (options.getIpVersion() == 4) {
+ if ((stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_DO) > 10) &&
+ ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_DO) /
+ stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_DO)) >=
+ options.getMaxDropPercentage()[0])) {
+ return(true);
+ }
+ } else if (options.getIpVersion() == 6) {
+ if ((stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA) > 10) &&
+ ((100. * stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_SA) /
+ stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA)) >=
+ options.getMaxDropPercentage()[0])) {
+ return(true);
+ }
+ }
+ }
+ // Check if we reached maximum drops percentage of ACK/REPLY packets.
+ if (options.getMaxDropPercentage().size() == 2) {
+ if (options.getIpVersion() == 4) {
+ if ((stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) > 10) &&
+ ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) /
+ stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA)) >=
+ options.getMaxDropPercentage()[0])) {
+ return(true);
+ }
+ } else if (options.getIpVersion() == 6) {
+ if ((stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR) > 10) &&
+ ((100. * stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_RR) /
+ stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR)) >=
+ options.getMaxDropPercentage()[0])) {
+ return(true);
+ }
+ }
}
return(false);
}
return opt;
}
-
-
std::vector<uint8_t>
TestControl::generateMacAddress() const {
CommandOptions& options = CommandOptions::instance();
return (0);
}
+uint64_t
+TestControl::getRcvdPacketsNum(const ExchangeType xchg_type) const {
+ uint8_t ip_version = CommandOptions::instance().getIpVersion();
+ if (ip_version == 4) {
+ return(stats_mgr4_->getRcvdPacketsNum(xchg_type));
+ }
+ return(stats_mgr6_->
+ getRcvdPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
+}
+
+uint64_t
+TestControl::getSentPacketsNum(const ExchangeType xchg_type) const {
+ uint8_t ip_version = CommandOptions::instance().getIpVersion();
+ if (ip_version == 4) {
+ return(stats_mgr4_->getSentPacketsNum(xchg_type));
+ }
+ return(stats_mgr6_->
+ getSentPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
+}
+
void
TestControl::initializeStatsMgr() {
CommandOptions& options = CommandOptions::instance();
stats_mgr4_.reset();
stats_mgr4_ = StatsMgr4Ptr(new StatsMgr4());
stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_DO);
- if (options.getExchangeMode() == CommandOptions::DO_SA) {
+ if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RA);
}
stats_mgr6_.reset();
stats_mgr6_ = StatsMgr6Ptr(new StatsMgr6());
stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_SA);
- if (options.getExchangeMode() == CommandOptions::DO_SA) {
+ if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR);
- }
+ }
}
}
}
void
-TestControl::receivePacket4(Pkt4Ptr& pkt4) {
+TestControl::receivePacket4(const TestControlSocket&,
+ const Pkt4Ptr& pkt4) {
switch(pkt4->getType()) {
case DHCPOFFER :
stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_DO, pkt4);
}
void
-TestControl::receivePacket6(Pkt6Ptr& pkt6) {
- switch(pkt6->getType()) {
- case DHCPV6_ADVERTISE :
- stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_SA, pkt6);
- break;
- case DHCPV6_REPLY :
+TestControl::receivePacket6(const TestControlSocket& socket,
+ const Pkt6Ptr& pkt6) {
+ uint8_t packet_type = pkt6->getType();
+ if (packet_type == DHCPV6_ADVERTISE) {
+ Pkt6Ptr solicit_pkt6(stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_SA,
+ pkt6));
+ if (solicit_pkt6) {
+ sendRequest6(socket, solicit_pkt6, pkt6);
+ }
+ } else if (packet_type == DHCPV6_REPLY) {
stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR, pkt6);
- break;
- default:
+ } else {
isc_throw(BadValue, "unknown type " << pkt6->getType()
<< " of received DHCPv6 packet");
}
-
}
void
-TestControl::receivePackets() {
+TestControl::receivePackets(const TestControlSocket& socket) {
int timeout = 0;
bool receiving = true;
while (receiving) {
receiving = false;
} else {
pkt4->unpack();
- receivePacket4(pkt4);
+ receivePacket4(socket, pkt4);
}
} else if (CommandOptions::instance().getIpVersion() == 6) {
Pkt6Ptr pkt6 = IfaceMgr::instance().receive6(timeout);
receiving = false;
} else {
if (pkt6->unpack()) {
- receivePacket6(pkt6);
+ receivePacket6(socket, pkt6);
}
}
}
}
registerOptionFactories();
TestControlSocket socket(openSocket());
-
+
initializeStatsMgr();
uint64_t packets_sent = 0;
for (;;) {
}
uint64_t packets_due = getNextExchangesNum();
- receivePackets();
+ receivePackets(socket);
for (uint64_t i = packets_due; i > 0; --i) {
if (options.getIpVersion() == 4) {
// Generate the MAC address to be passed in the packet.
std::vector<uint8_t> mac_address = generateMacAddress();
// Generate trasnaction id to be set for the new exchange.
- const uint32_t transid = static_cast<uint32_t>(random() % 0x00FFFFFF);
- boost::shared_ptr<Pkt4> pkt4(new Pkt4(DHCPDISCOVER, transid));
+ const uint32_t transid = generateTransid();
+ Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, transid));
if (!pkt4) {
isc_throw(Unexpected, "failed to create DISCOVER packet");
}
stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO, pkt4);
}
+void
+TestControl::sendRequest6(const TestControlSocket& socket,
+ const Pkt6Ptr& solicit_pkt6,
+ const Pkt6Ptr& advertise_pkt6) {
+ const uint32_t transid = static_cast<uint32_t>(random() % 0x00FFFFFF);
+ Pkt6Ptr pkt6(new Pkt6(DHCPV6_REQUEST, transid));
+ // Calculate elapsed time
+ ptime solicit_time = solicit_pkt6->getTimestamp();
+ ptime advertise_time = advertise_pkt6->getTimestamp();
+ if (solicit_time.is_not_a_date_time()) {
+ isc_throw(Unexpected, "timestamp was not set for SOLICIT packet");
+ }
+ if (advertise_time.is_not_a_date_time()) {
+ isc_throw(Unexpected, "timestamp was not set for ADVERTISE packet");
+ }
+ time_period period(solicit_time, advertise_time);
+ if (period.is_null()) {
+ pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME));
+ } else {
+ OptionBuffer buf();
+ const uint32_t elapsed_time = period.length().total_seconds();
+ OptionPtr opt_elapsed_time =
+ Option::factory(Option::V6, D6O_ELAPSED_TIME);
+ opt_elapsed_time->setUint16(static_cast<uint16_t>(elapsed_time));
+ pkt6->addOption(opt_elapsed_time);
+ }
+ OptionPtr opt_clientid = advertise_pkt6->getOption(D6O_CLIENTID);
+ if (!opt_clientid) {
+ isc_throw(Unexpected, "client id not found in received packet");
+ }
+ pkt6->addOption(opt_clientid);
+ OptionPtr opt_serverid = advertise_pkt6->getOption(D6O_SERVERID);
+ if (!opt_serverid) {
+ isc_throw(Unexpected, "server id not found in received packet");
+ }
+ pkt6->addOption(opt_serverid);
+ 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);
+ setDefaults6(socket, pkt6);
+
+ pkt6->pack();
+ IfaceMgr::instance().send(pkt6);
+ if (!stats_mgr6_) {
+ isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
+ "hasn't been initialized");
+ }
+ stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6);
+}
+
void
TestControl::sendSolicit6(const TestControlSocket& socket) {
++sent_packets_0_;
// Generate DUID to be passed to the packet
std::vector<uint8_t> duid = generateDuid();
// Generate trasnaction id to be set for the new exchange.
- const uint32_t transid = static_cast<uint32_t>(random() % 0x00FFFFFF);
- boost::shared_ptr<Pkt6> pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
+ const uint32_t transid = generateTransid();
+ Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
if (!pkt6) {
isc_throw(Unexpected, "failed to create SOLICIT packet");
}
pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME));
- pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT));
+ // pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT));
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));
void
TestControl::setDefaults4(const TestControlSocket& socket,
- const boost::shared_ptr<Pkt4>& pkt) {
+ const Pkt4Ptr& pkt) {
CommandOptions& options = CommandOptions::instance();
// Interface name.
pkt->setIface(socket.getIface());
void
TestControl::setDefaults6(const TestControlSocket& socket,
- const boost::shared_ptr<Pkt6>& pkt) {
+ const Pkt6Ptr& pkt) {
CommandOptions& options = CommandOptions::instance();
// Interface name.
pkt->setIface(socket.getIface());
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <dhcp/dhcp6.h>
typedef StatsMgr<dhcp::Pkt6> StatsMgr6;
// Pointer to Statistics Manager for DHCPv6.
typedef boost::shared_ptr<StatsMgr6> StatsMgr6Ptr;
+ // Packet exchange type.
+ typedef StatsMgr<>::ExchangeType ExchangeType;
/// \brief Socket wrapper class.
///
asiolink::IOAddress addr_; ///< Address bound.
};
+ /// \brief Default transaction id generator class.
+ ///
+ /// This is default transaction id generator class. The member
+ /// function is used to generate unique transaction id value.
+ /// Other generator classes should derive from this one to
+ /// override the standard generation algorithm (e.g. unit tests
+ /// override this class wih algorithm that produces more predictable
+ /// transaction id values).
+ class TransidGenerator {
+ public:
+ /// \brief generate transaction id.
+ ///
+ /// \return generated transazction id value.
+ virtual uint32_t generate() {
+ return static_cast<uint32_t>(random() % 0x00FFFFFF);
+ }
+ };
+
+ typedef boost::shared_ptr<TransidGenerator> TransidGeneratorPtr;
+
/// \brief Length of the Ethernet HW address (MAC) in bytes.
static const uint8_t HW_ETHER_LEN = 6;
/// \return the only existing instance of test control
static TestControl& instance();
- /// Run performance test.
+ /// brief\ Run performance test.
///
/// Method runs whole performance test. Command line options must
/// be parsed prior to running this function. Othewise function will
/// \throw isc::Unexpected if internal Test Controler error occured.
void run();
+ /// \brief Set new transaction id generator.
+ ///
+ /// \param generator generator object to be used.
+ void setTransidGenerator(TransidGeneratorPtr& generator) {
+ transid_gen_.reset();
+ transid_gen_ = generator;
+ }
+
protected:
// We would really like these methods and members to be private but
/// \return generated MAC address.
std::vector<uint8_t> generateMacAddress() const;
+ /// \brief generate transaction id.
+ ///
+ /// \return generated transaction id.
+ uint32_t generateTransid() {
+ return(transid_gen_->generate());
+ }
+
/// \brief Returns number of exchanges to be started.
///
/// Method returns number of new exchanges to be started as soon
/// not initialized.
void printStats() const;
- void receivePacket4(dhcp::Pkt4Ptr& pkt4);
+ /// \brief Receive DHCPv4 packet.
+ ///
+ /// Method performs reception of the DHCPv4 packet, updates
+ /// statistics and responsds to the server if required, e.g.
+ /// when OFFER packet arrives, this function will initiate
+ /// REQUEST message to the server.
+ ///
+ /// \param socket socket to be used.
+ /// \param pkt4 object representing DHCPv4 packet received.
+ /// \throw isc::BadValue if unknown message type received.
+ /// \throw isc::Unexpected if unexpected error occured.
+ void receivePacket4(const TestControlSocket& socket,
+ const dhcp::Pkt4Ptr& pkt4);
- void receivePacket6(dhcp::Pkt6Ptr& pkt4);
+ /// \brief Receive DHCPv6 packet.
+ ///
+ /// Method performs reception of the DHCPv6 packet, updates
+ /// statistics and responsds to the server if required, e.g.
+ /// when ADVERTISE packet arrives, this function will initiate
+ /// REQUEST message to the server.
+ ///
+ /// \param socket socket to be used.
+ /// \param pkt6 object representing DHCPv6 packet received.
+ /// \throw isc::BadValue if unknown message type received.
+ /// \throw isc::Unexpected if unexpected error occured.
+ void receivePacket6(const TestControlSocket& socket,
+ const dhcp::Pkt6Ptr& pkt6);
/// \brief Receive DHCPv4 or DHCPv6 packets from the server.
///
/// Method receives DHCPv4 or DHCPv6 packets from the server.
- void receivePackets();
+ /// This function will call \ref receivePacket4 or
+ /// \ref receivePacket6 depending if DHCPv4 or DHCPv6 packet
+ /// has arrived.
+ ///
+ /// \param socket socket to be used.
+ /// \throw::BadValue if unknown message type received.
+ /// \throw::Unexpected if unexpected error occured.
+ void receivePackets(const TestControlSocket& socket);
/// \brief Register option factory functions for DHCPv4
///
/// \throw isc::BadValue if MAC address has invalid length.
void sendDiscover4(const TestControlSocket& socket);
+ /// \brief Sent DHCPv6 REQUEST message.
+ ///
+ /// Method creates and sends DHCPv6 REQUEST message to the server
+ /// with the following options:
+ /// - D6O_ELAPSED_TIME
+ /// - D6O_CLIENTID
+ /// - D6O_SERVERID
+ /// The elapsed time is calculated based on the duration between
+ /// sending a SOLICIT and receiving the ADVERTISE packet prior.
+ /// For this reason both solicit and advertise packet objects have
+ /// to be passed when calling this function.
+ ///
+ /// \param socket socket to be used to send message.
+ /// \param solicit_pkt6 SOLICIT packet object.
+ /// \param advertise_pkt6 ADVERTISE packet object.
+ /// \throw isc::Unexpected if unexpected error occured.
+ /// \throw isc::InvalidOperation if Statistics Manager has not been
+ /// initialized.
+ void sendRequest6(const TestControlSocket& socket,
+ const dhcp::Pkt6Ptr& solicit_pkt6,
+ const dhcp::Pkt6Ptr& advertise_pkt6);
+
/// \brief Send DHCPv6 SOLICIT message.
///
/// Method creates and sends DHCPv6 SOLICIT message to the server
/// \param socket socket used to send the packet.
/// \param pkt reference to packet to be configured.
void setDefaults4(const TestControlSocket& socket,
- const boost::shared_ptr<dhcp::Pkt4>& pkt);
+ const dhcp::Pkt4Ptr& pkt);
/// \brief Set default DHCPv6 packet parameters.
///
/// \param socket socket used to send the packet.
/// \param pkt reference to packet to be configured.
void setDefaults6(const TestControlSocket& socket,
- const boost::shared_ptr<dhcp::Pkt6>& pkt);
+ const dhcp::Pkt6Ptr& pkt);
/// \brief Update due time to initiate next chunk of exchanges.
///
private:
+ /// \brief Generate transaction id using random function.
+ ///
+ /// \return generated transaction id value.
+ static uint32_t generateTransidRandom();
+
+ /// \brief Get number of received packets.
+ ///
+ /// Get the number of received packets from the Statistics Manager.
+ /// Function may throw if Statistics Manager object is not
+ /// initialized.
+ /// \param xchg_type packet exchange type.
+ /// \return number of received packets.
+ uint64_t getRcvdPacketsNum(const ExchangeType xchg_type) const;
+
+ /// \brief Get number of sent packets.
+ ///
+ /// Get the number of sent packets from the Statistics Manager.
+ /// Function may throw if Statistics Manager object is not
+ /// initialized.
+ /// \param xchg_type packet exchange type.
+ /// \return number of sent packets.
+ uint64_t getSentPacketsNum(const ExchangeType xchg_type) const;
+
boost::posix_time::ptime send_due_; ///< Due time to initiate next chunk
///< of exchanges.
boost::posix_time::ptime last_sent_; ///< Indicates when the last exchange
/// was initiated.
+ StatsMgr4Ptr stats_mgr4_; ///< Statistics Manager 4.
+ StatsMgr6Ptr stats_mgr6_; ///< Statistics Manager 6.
- StatsMgr4Ptr stats_mgr4_; /// Statistics Manager 4.
- StatsMgr6Ptr stats_mgr6_; /// Statistics Manager 6.
+ // Pointers to functions.
+ TransidGeneratorPtr transid_gen_; ///< Transaction id generator.
uint64_t sent_packets_0_;
uint64_t sent_packets_1_;
/// to allow unit testing.
class NakedTestControl: public TestControl {
public:
+
+ /// \brief Incremental transaction id generaator.
+ ///
+ /// This is incremental transaction id generator. It overrides
+ /// the default transaction id generator that generates transaction
+ /// ids using random function. This generator will generate values
+ /// like: 1,2,3 etc.
+ class IncrementalGenerator : public TestControl::TransidGenerator {
+ public:
+ /// \brief Default constructor.
+ IncrementalGenerator() :
+ TransidGenerator(),
+ transid_(0) {
+ }
+
+ /// \brief Generate unique transaction id.
+ ///
+ /// Generate unique transaction ids incrementally:
+ /// 1,2,3,4 etc.
+ ///
+ /// \return generated transaction id.
+ virtual uint32_t generate() {
+ return ++transid_;
+ }
+ private:
+ uint32_t transid_; ///< Last generated transaction id.
+ };
+
using TestControl::checkExitConditions;
using TestControl::factoryElapsedTime6;
using TestControl::factoryGeneric;
using TestControl::generateDuid;
using TestControl::generateMacAddress;
using TestControl::getNextExchangesNum;
+ using TestControl::initializeStatsMgr;
using TestControl::openSocket;
- using TestControl::receivePackets;
+ using TestControl::receivePacket4;
+ using TestControl::receivePacket6;
using TestControl::registerOptionFactories;
using TestControl::sendDiscover4;
using TestControl::sendSolicit6;
using TestControl::setDefaults4;
using TestControl::setDefaults6;
- using TestControl::updateSendDue;
NakedTestControl() : TestControl() { };
/// \brief Default Constructor
TestControlTest() { }
+ static uint32_t generateTransidIncremental() {
+ static uint32_t transid(1);
+ return ++transid;
+ }
+
/// \brief Get local loopback interface name.
///
/// Scan available network interfaces for local loopback
}
}
+ /// \brief Test DHCPv4 exchanges.
+ ///
+ /// Function simulates DHCPv4 exchanges. Function caller specifies
+ /// number of exchanges to be simulated and number of simulated
+ /// responses. When number of responses is lower than number of
+ /// iterations than the difference between them is the number
+ /// of simulated packet drops. This is useful to test if program
+ /// exit conditions are handled properly (maximum number of packet
+ /// drops specified as -D<max-drops> is taken into account).
+ ///
+ /// \param iterations_num number of exchanges to simulate.
+ /// \param receive_num number of received OFFER packets.
+ /// \param iterations_performed actual number of iterations.
+ void testPkt4Exchange(int iterations_num,
+ int receive_num,
+ int& iterations_performed) const {
+ uint16_t port = 10547;
+ int sock_handle = 0;
+ NakedTestControl tc;
+ tc.initializeStatsMgr();
+ // Incremental transaction id generator will generate
+ // predictable values of transaction id for each iteration.
+ // This is important because we need to simulate reponses
+ // from the server and use the same transaction ids as in
+ // packets sent by client.
+ TestControl::TransidGeneratorPtr
+ generator(new NakedTestControl::IncrementalGenerator());
+ tc.setTransidGenerator(generator);
+ // Socket is needed to send packets through the interface.
+ ASSERT_NO_THROW(sock_handle = tc.openSocket(port));
+ TestControl::TestControlSocket sock(sock_handle);
+ int i = 0;
+ for (; i < iterations_num; ++i) {
+ if (tc.checkExitConditions()) {
+ break;
+ }
+ ASSERT_NO_THROW(tc.sendDiscover4(sock));
+ // Do not simulate responses for packets later
+ // that specified as receive_num. This simulates
+ // packet drops.
+ if (i - 1 < receive_num) {
+ boost::shared_ptr<Pkt4> offer_pkt4(createOfferPkt4(i));
+ // Receive OFFER and send REQUEST.
+ ASSERT_NO_THROW(tc.receivePacket4(sock, offer_pkt4));
+ }
+ }
+ // Return the number of iterations performed.
+ iterations_performed = i;
+ }
+
+ /// \brief Test DHCPv6 exchanges.
+ ///
+ /// Function simulates DHCPv6 exchanges. Function caller specifies
+ /// number of exchanges to be simulated and number of simulated
+ /// responses. When number of responses is lower than number of
+ /// iterations than the difference between them is the number
+ /// of simulated packet drops. This is useful to test if program
+ /// exit conditions are handled properly (maximum number of packet
+ /// drops specified as -D<max-drops> is taken into account).
+ ///
+ /// \param iterations_num number of exchanges to simulate.
+ /// \param receive_num number of received OFFER packets.
+ /// \param iterations_performed actual number of iterations.
+ void testPkt6Exchange(int iterations_num,
+ int receive_num,
+ int& iterations_performed) const {
+ uint16_t port = 10547;
+ int sock_handle = 0;
+ NakedTestControl tc;
+ tc.initializeStatsMgr();
+ // Incremental transaction id generator will generate
+ // predictable values of transaction id for each iteration.
+ // This is important because we need to simulate reponses
+ // from the server and use the same transaction ids as in
+ // packets sent by client.
+ TestControl::TransidGeneratorPtr
+ generator(new NakedTestControl::IncrementalGenerator());
+ tc.setTransidGenerator(generator);
+ // Socket is needed to send packets through the interface.
+ ASSERT_NO_THROW(sock_handle = tc.openSocket(port));
+ TestControl::TestControlSocket sock(sock_handle);
+ int i = 0;
+ for (; i < iterations_num; ++i) {
+ if (tc.checkExitConditions()) {
+ break;
+ }
+ // Do not simulate responses for packets later
+ // that specified as receive_num. This simulates
+ // packet drops.
+ ASSERT_NO_THROW(tc.sendSolicit6(sock));
+ if (i - 1 < receive_num) {
+ boost::shared_ptr<Pkt6> advertise_pkt6(createAdvertisePkt6(i));
+ // Receive ADVERTISE and send REQUEST.
+ ASSERT_NO_THROW(tc.receivePacket6(sock, advertise_pkt6));
+ }
+
+ }
+ // Return the number of iterations performed.
+ iterations_performed = i;
+ }
+
/// \brief Test generation of multiple MAC addresses.
///
/// This method validates generation of multiple MAC addresses.
CommandOptionsHelper::process(cmdline);
}
+private:
+ boost::shared_ptr<Pkt4>
+ createOfferPkt4(uint32_t transid) const {
+ boost::shared_ptr<Pkt4> offer(new Pkt4(DHCPOFFER, transid));
+ OptionPtr opt_msg_type = Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE,
+ OptionBuffer(DHCPOFFER));
+ offer->setYiaddr(asiolink::IOAddress("127.0.0.1"));
+ offer->addOption(opt_msg_type);
+ offer->updateTimestamp();
+ return(offer);
+ }
+
+ boost::shared_ptr<Pkt6>
+ createAdvertisePkt6(uint32_t transid) const {
+ OptionPtr opt_ia_na = Option::factory(Option::V6, D6O_IA_NA);
+ OptionPtr opt_serverid(new Option(Option::V6, D6O_SERVERID));
+ NakedTestControl tc;
+ std::vector<uint8_t> duid(tc.generateDuid());
+ OptionPtr opt_clientid(Option::factory(Option::V6, D6O_CLIENTID, duid));
+ boost::shared_ptr<Pkt6> advertise(new Pkt6(DHCPV6_ADVERTISE, transid));
+ advertise->addOption(opt_ia_na);
+ advertise->addOption(opt_serverid);
+ advertise->addOption(opt_clientid);
+ advertise->updateTimestamp();
+ return(advertise);
+ }
+
};
TEST_F(TestControlTest, GenerateDuid) {
}
}
+TEST_F(TestControlTest, Packet4Exchange) {
+ // Get the local loopback interface to open socket on
+ // it and test packets exchanges. We don't want to fail
+ // the test if interface is not available.
+ std::string loopback_iface(getLocalLoopback());
+ if (loopback_iface.empty()) {
+ std::cout << "Unable to find the loopback interface. Skip test."
+ << std::endl;
+ return;
+ }
+
+ // Set number of iterations to some high value.
+ const int iterations_num = 100;
+ processCmdLine("perfdhcp -l " + loopback_iface
+ + " -r 100 -n 10 -R 20 127.0.0.1");
+ // The actual number of iterations will be stored in the
+ // following variable.
+ int iterations_performed = 0;
+ testPkt4Exchange(iterations_num, iterations_num, iterations_performed);
+ // The command line restricts the number of iterations to 10
+ // with -n 10 parameter.
+ EXPECT_EQ(10, iterations_performed);
+
+ // With the following command line we restrict the maximum
+ // number of dropped packets to 20% of all.
+ processCmdLine("perfdhcp -l " + loopback_iface
+ + " -r 100 -R 20 -n 20 -D 10% 127.0.0.1");
+ // The number iterations is restricted by the percentage of
+ // dropped packets (-D 10%). We also have to bump up the number
+ // of iterations because the percentage limitation checks starts
+ // at packet #10. We expect that at packet #12 the 10% threshold
+ // will be reached.
+ const int received_num = 10;
+ testPkt4Exchange(iterations_num, received_num, iterations_performed);
+ EXPECT_EQ(12, iterations_performed);
+}
+
+TEST_F(TestControlTest, Packet6Exchange) {
+ // Get the local loopback interface to open socket on
+ // it and test packets exchanges. We don't want to fail
+ // the test if interface is not available.
+ std::string loopback_iface(getLocalLoopback());
+ if (loopback_iface.empty()) {
+ std::cout << "Unable to find the loopback interface. Skip test."
+ << std::endl;
+ return;
+ }
+
+ const int iterations_num = 100;
+ // Set number of iterations to 10.
+ processCmdLine("perfdhcp -l " + loopback_iface
+ + " -6 -r 100 -n 10 -R 20 ::1");
+ int iterations_performed = 0;
+ // Set number of received packets equal to number of iterations.
+ // This simulates no packet drops.
+ testPkt6Exchange(iterations_num, iterations_num, iterations_performed);
+ // Actual number of iterations should be 10.
+ EXPECT_EQ(10, iterations_performed);
+
+ // The maximum number of dropped packets is 3 (because of -D 3).
+ processCmdLine("perfdhcp -l " + loopback_iface
+ + " -6 -r 100 -n 10 -R 20 -D 3 ::1");
+ // For the first 3 packets we are simulating responses from server.
+ // For other packets we don't so packet as 4,5,6 will be dropped and
+ // then test should be interrupted and actual number of iterations will
+ // be 6.
+ const int received_num = 3;
+ testPkt6Exchange(iterations_num, received_num, iterations_performed);
+ EXPECT_EQ(6, iterations_performed);
+}
+
TEST_F(TestControlTest, RateControl) {
// We don't specify the exchange rate here so the aggressivity
// value will determine how many packets are to be send each