mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 14:05:33 +00:00
Merge branch 'trac1959'
This commit is contained in:
@@ -1223,6 +1223,7 @@ AC_CONFIG_FILES([Makefile
|
|||||||
tests/tools/badpacket/tests/Makefile
|
tests/tools/badpacket/tests/Makefile
|
||||||
tests/tools/perfdhcp/Makefile
|
tests/tools/perfdhcp/Makefile
|
||||||
tests/tools/perfdhcp/tests/Makefile
|
tests/tools/perfdhcp/tests/Makefile
|
||||||
|
tests/tools/perfdhcp/templates/Makefile
|
||||||
dns++.pc
|
dns++.pc
|
||||||
])
|
])
|
||||||
AC_OUTPUT([doc/version.ent
|
AC_OUTPUT([doc/version.ent
|
||||||
|
@@ -50,6 +50,15 @@ IfaceMgr::Iface::Iface(const std::string& name, int ifindex)
|
|||||||
memset(mac_, 0, sizeof(mac_));
|
memset(mac_, 0, sizeof(mac_));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
IfaceMgr::Iface::closeSockets() {
|
||||||
|
for (SocketCollection::iterator sock = sockets_.begin();
|
||||||
|
sock != sockets_.end(); ++sock) {
|
||||||
|
close(sock->sockfd_);
|
||||||
|
}
|
||||||
|
sockets_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
IfaceMgr::Iface::getFullName() const {
|
IfaceMgr::Iface::getFullName() const {
|
||||||
ostringstream tmp;
|
ostringstream tmp;
|
||||||
@@ -138,15 +147,8 @@ IfaceMgr::IfaceMgr()
|
|||||||
void IfaceMgr::closeSockets() {
|
void IfaceMgr::closeSockets() {
|
||||||
for (IfaceCollection::iterator iface = ifaces_.begin();
|
for (IfaceCollection::iterator iface = ifaces_.begin();
|
||||||
iface != ifaces_.end(); ++iface) {
|
iface != ifaces_.end(); ++iface) {
|
||||||
|
iface->closeSockets();
|
||||||
for (SocketCollection::iterator sock = iface->sockets_.begin();
|
|
||||||
sock != iface->sockets_.end(); ++sock) {
|
|
||||||
cout << "Closing socket " << sock->sockfd_ << endl;
|
|
||||||
close(sock->sockfd_);
|
|
||||||
}
|
|
||||||
iface->sockets_.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IfaceMgr::~IfaceMgr() {
|
IfaceMgr::~IfaceMgr() {
|
||||||
@@ -477,11 +479,34 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
|
|||||||
asio::io_service io_service;
|
asio::io_service io_service;
|
||||||
asio::ip::udp::socket sock(io_service);
|
asio::ip::udp::socket sock(io_service);
|
||||||
|
|
||||||
// Try to connect to remote endpoint and check if attempt is successful.
|
|
||||||
asio::error_code err_code;
|
asio::error_code err_code;
|
||||||
|
// If remote address is broadcast address we have to
|
||||||
|
// allow this on the socket.
|
||||||
|
if (remote_addr.getAddress().is_v4() &&
|
||||||
|
(remote_addr == IOAddress("255.255.255.255"))) {
|
||||||
|
// Socket has to be open prior to setting the broadcast
|
||||||
|
// option. Otherwise set_option will complain about
|
||||||
|
// bad file descriptor.
|
||||||
|
|
||||||
|
// @todo: We don't specify interface in any way here. 255.255.255.255
|
||||||
|
// We can very easily end up with a socket working on a different
|
||||||
|
// interface.
|
||||||
|
sock.open(asio::ip::udp::v4(), err_code);
|
||||||
|
if (err_code) {
|
||||||
|
isc_throw(Unexpected, "failed to open UDPv4 socket");
|
||||||
|
}
|
||||||
|
sock.set_option(asio::socket_base::broadcast(true), err_code);
|
||||||
|
if (err_code) {
|
||||||
|
sock.close();
|
||||||
|
isc_throw(Unexpected, "failed to enable broadcast on the socket");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to connect to remote endpoint and check if attempt is successful.
|
||||||
sock.connect(remote_endpoint->getASIOEndpoint(), err_code);
|
sock.connect(remote_endpoint->getASIOEndpoint(), err_code);
|
||||||
if (err_code) {
|
if (err_code) {
|
||||||
isc_throw(Unexpected,"Failed to connect to remote endpoint.");
|
sock.close();
|
||||||
|
isc_throw(Unexpected,"failed to connect to remote endpoint.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Once we are connected socket object holds local endpoint.
|
// Once we are connected socket object holds local endpoint.
|
||||||
@@ -489,6 +514,9 @@ IfaceMgr::getLocalAddress(const IOAddress& remote_addr, const uint16_t port) {
|
|||||||
sock.local_endpoint();
|
sock.local_endpoint();
|
||||||
asio::ip::address local_address(local_endpoint.address());
|
asio::ip::address local_address(local_endpoint.address());
|
||||||
|
|
||||||
|
// Close the socket.
|
||||||
|
sock.close();
|
||||||
|
|
||||||
// Return address of local endpoint.
|
// Return address of local endpoint.
|
||||||
return IOAddress(local_address);
|
return IOAddress(local_address);
|
||||||
}
|
}
|
||||||
@@ -546,8 +574,9 @@ int IfaceMgr::openSocket6(Iface& iface, const IOAddress& addr, uint16_t port) {
|
|||||||
memset(&addr6, 0, sizeof(addr6));
|
memset(&addr6, 0, sizeof(addr6));
|
||||||
addr6.sin6_family = AF_INET6;
|
addr6.sin6_family = AF_INET6;
|
||||||
addr6.sin6_port = htons(port);
|
addr6.sin6_port = htons(port);
|
||||||
if (addr.toText() != "::1")
|
if (addr.toText() != "::1") {
|
||||||
addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str());
|
addr6.sin6_scope_id = if_nametoindex(iface.getName().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(&addr6.sin6_addr,
|
memcpy(&addr6.sin6_addr,
|
||||||
addr.getAddress().to_v6().to_bytes().data(),
|
addr.getAddress().to_v6().to_bytes().data(),
|
||||||
@@ -724,7 +753,6 @@ IfaceMgr::send(const Pkt6Ptr& pkt) {
|
|||||||
bool
|
bool
|
||||||
IfaceMgr::send(const Pkt4Ptr& pkt)
|
IfaceMgr::send(const Pkt4Ptr& pkt)
|
||||||
{
|
{
|
||||||
|
|
||||||
Iface* iface = getIface(pkt->getIface());
|
Iface* iface = getIface(pkt->getIface());
|
||||||
if (!iface) {
|
if (!iface) {
|
||||||
isc_throw(BadValue, "Unable to send Pkt4. Invalid interface ("
|
isc_throw(BadValue, "Unable to send Pkt4. Invalid interface ("
|
||||||
@@ -800,8 +828,9 @@ IfaceMgr::receive4(uint32_t timeout) {
|
|||||||
/// provided set to indicated which sockets have something to read.
|
/// provided set to indicated which sockets have something to read.
|
||||||
for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
|
for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
|
||||||
|
|
||||||
for (SocketCollection::const_iterator s = iface->sockets_.begin();
|
const SocketCollection& socket_collection = iface->getSockets();
|
||||||
s != iface->sockets_.end(); ++s) {
|
for (SocketCollection::const_iterator s = socket_collection.begin();
|
||||||
|
s != socket_collection.end(); ++s) {
|
||||||
|
|
||||||
// Only deal with IPv4 addresses.
|
// Only deal with IPv4 addresses.
|
||||||
if (s->addr_.getFamily() == AF_INET) {
|
if (s->addr_.getFamily() == AF_INET) {
|
||||||
@@ -864,8 +893,9 @@ IfaceMgr::receive4(uint32_t timeout) {
|
|||||||
|
|
||||||
// Let's find out which interface/socket has the data
|
// Let's find out which interface/socket has the data
|
||||||
for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
|
for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
|
||||||
for (SocketCollection::const_iterator s = iface->sockets_.begin();
|
const SocketCollection& socket_collection = iface->getSockets();
|
||||||
s != iface->sockets_.end(); ++s) {
|
for (SocketCollection::const_iterator s = socket_collection.begin();
|
||||||
|
s != socket_collection.end(); ++s) {
|
||||||
if (FD_ISSET(s->sockfd_, &sockets)) {
|
if (FD_ISSET(s->sockfd_, &sockets)) {
|
||||||
candidate = &(*s);
|
candidate = &(*s);
|
||||||
break;
|
break;
|
||||||
@@ -967,9 +997,9 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout) {
|
|||||||
/// provided set to indicated which sockets have something to read.
|
/// provided set to indicated which sockets have something to read.
|
||||||
IfaceCollection::const_iterator iface;
|
IfaceCollection::const_iterator iface;
|
||||||
for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
|
for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
|
||||||
|
const SocketCollection& socket_collection = iface->getSockets();
|
||||||
for (SocketCollection::const_iterator s = iface->sockets_.begin();
|
for (SocketCollection::const_iterator s = socket_collection.begin();
|
||||||
s != iface->sockets_.end(); ++s) {
|
s != socket_collection.end(); ++s) {
|
||||||
|
|
||||||
// Only deal with IPv4 addresses.
|
// Only deal with IPv4 addresses.
|
||||||
if (s->addr_.getFamily() == AF_INET6) {
|
if (s->addr_.getFamily() == AF_INET6) {
|
||||||
@@ -1032,8 +1062,9 @@ Pkt6Ptr IfaceMgr::receive6(uint32_t timeout) {
|
|||||||
|
|
||||||
// Let's find out which interface/socket has the data
|
// Let's find out which interface/socket has the data
|
||||||
for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
|
for (iface = ifaces_.begin(); iface != ifaces_.end(); ++iface) {
|
||||||
for (SocketCollection::const_iterator s = iface->sockets_.begin();
|
const SocketCollection& socket_collection = iface->getSockets();
|
||||||
s != iface->sockets_.end(); ++s) {
|
for (SocketCollection::const_iterator s = socket_collection.begin();
|
||||||
|
s != socket_collection.end(); ++s) {
|
||||||
if (FD_ISSET(s->sockfd_, &sockets)) {
|
if (FD_ISSET(s->sockfd_, &sockets)) {
|
||||||
candidate = &(*s);
|
candidate = &(*s);
|
||||||
break;
|
break;
|
||||||
@@ -1168,8 +1199,9 @@ uint16_t IfaceMgr::getSocket(const isc::dhcp::Pkt6& pkt) {
|
|||||||
<< pkt.getIface());
|
<< pkt.getIface());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SocketCollection& socket_collection = iface->getSockets();
|
||||||
SocketCollection::const_iterator s;
|
SocketCollection::const_iterator s;
|
||||||
for (s = iface->sockets_.begin(); s != iface->sockets_.end(); ++s) {
|
for (s = socket_collection.begin(); s != socket_collection.end(); ++s) {
|
||||||
if ((s->family_ == AF_INET6) &&
|
if ((s->family_ == AF_INET6) &&
|
||||||
(!s->addr_.getAddress().to_v6().is_multicast())) {
|
(!s->addr_.getAddress().to_v6().is_multicast())) {
|
||||||
return (s->sockfd_);
|
return (s->sockfd_);
|
||||||
@@ -1190,8 +1222,9 @@ uint16_t IfaceMgr::getSocket(isc::dhcp::Pkt4 const& pkt) {
|
|||||||
<< pkt.getIface());
|
<< pkt.getIface());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SocketCollection& socket_collection = iface->getSockets();
|
||||||
SocketCollection::const_iterator s;
|
SocketCollection::const_iterator s;
|
||||||
for (s = iface->sockets_.begin(); s != iface->sockets_.end(); ++s) {
|
for (s = socket_collection.begin(); s != socket_collection.end(); ++s) {
|
||||||
if (s->family_ == AF_INET) {
|
if (s->family_ == AF_INET) {
|
||||||
return (s->sockfd_);
|
return (s->sockfd_);
|
||||||
}
|
}
|
||||||
|
@@ -72,8 +72,10 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// type that holds a list of socket informations
|
/// type that holds a list of socket informations
|
||||||
|
/// @todo: Add SocketCollectionConstIter type
|
||||||
typedef std::list<SocketInfo> SocketCollection;
|
typedef std::list<SocketInfo> SocketCollection;
|
||||||
|
|
||||||
|
|
||||||
/// @brief represents a single network interface
|
/// @brief represents a single network interface
|
||||||
///
|
///
|
||||||
/// Iface structure represents network interface with all useful
|
/// Iface structure represents network interface with all useful
|
||||||
@@ -89,6 +91,9 @@ public:
|
|||||||
/// @param ifindex interface index (unique integer identifier)
|
/// @param ifindex interface index (unique integer identifier)
|
||||||
Iface(const std::string& name, int ifindex);
|
Iface(const std::string& name, int ifindex);
|
||||||
|
|
||||||
|
/// @brief Closes all open sockets on interface.
|
||||||
|
void closeSockets();
|
||||||
|
|
||||||
/// @brief Returns full interface name as "ifname/ifindex" string.
|
/// @brief Returns full interface name as "ifname/ifindex" string.
|
||||||
///
|
///
|
||||||
/// @return string with interface name
|
/// @return string with interface name
|
||||||
@@ -192,11 +197,25 @@ public:
|
|||||||
/// @return true if there was such socket, false otherwise
|
/// @return true if there was such socket, false otherwise
|
||||||
bool delSocket(uint16_t sockfd);
|
bool delSocket(uint16_t sockfd);
|
||||||
|
|
||||||
/// socket used to sending data
|
/// @brief Returns collection of all sockets added to interface.
|
||||||
/// TODO: this should be protected
|
///
|
||||||
SocketCollection sockets_;
|
/// When new socket is created with @ref IfaceMgr::openSocket
|
||||||
|
/// it is added to sockets collection on particular interface.
|
||||||
|
/// If socket is opened by other means (e.g. function that does
|
||||||
|
/// not use @ref IfaceMgr::openSocket) it will not be available
|
||||||
|
/// in this collection. Note that functions like
|
||||||
|
/// @ref IfaceMgr::openSocketFromIface use
|
||||||
|
/// @ref IfaceMgr::openSocket internally.
|
||||||
|
/// The returned reference is only valid during the lifetime of
|
||||||
|
/// the IfaceMgr object that returned it.
|
||||||
|
///
|
||||||
|
/// @return collection of sockets added to interface
|
||||||
|
const SocketCollection& getSockets() const { return sockets_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/// socket used to sending data
|
||||||
|
SocketCollection sockets_;
|
||||||
|
|
||||||
/// network interface name
|
/// network interface name
|
||||||
std::string name_;
|
std::string name_;
|
||||||
|
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
#include <boost/shared_array.hpp>
|
#include <boost/shared_array.hpp>
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
#include <util/buffer.h>
|
#include <util/buffer.h>
|
||||||
|
#include <exceptions/exceptions.h>
|
||||||
#include <dhcp/libdhcp++.h>
|
#include <dhcp/libdhcp++.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <dhcp/dhcp4.h>
|
#include <dhcp/dhcp4.h>
|
||||||
@@ -34,6 +35,31 @@ std::map<unsigned short, Option::Factory*> LibDHCP::v4factories_;
|
|||||||
std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
|
std::map<unsigned short, Option::Factory*> LibDHCP::v6factories_;
|
||||||
|
|
||||||
|
|
||||||
|
OptionPtr
|
||||||
|
LibDHCP::optionFactory(Option::Universe u,
|
||||||
|
uint16_t type,
|
||||||
|
const OptionBuffer& buf) {
|
||||||
|
FactoryMap::iterator it;
|
||||||
|
if (u == Option::V4) {
|
||||||
|
it = v4factories_.find(type);
|
||||||
|
if (it == v4factories_.end()) {
|
||||||
|
isc_throw(BadValue, "factory function not registered "
|
||||||
|
"for DHCP v4 option type " << type);
|
||||||
|
}
|
||||||
|
} else if (u == Option::V6) {
|
||||||
|
it = v6factories_.find(type);
|
||||||
|
if (it == v6factories_.end()) {
|
||||||
|
isc_throw(BadValue, "factory function not registered "
|
||||||
|
"for DHCPv6 option type " << type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isc_throw(BadValue, "invalid universe specified (expected "
|
||||||
|
"Option::V4 or Option::V6");
|
||||||
|
}
|
||||||
|
return (it->second(u, type, buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
|
size_t LibDHCP::unpackOptions6(const OptionBuffer& buf,
|
||||||
isc::dhcp::Option::OptionCollection& options) {
|
isc::dhcp::Option::OptionCollection& options) {
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
|
@@ -25,6 +25,26 @@ namespace dhcp {
|
|||||||
class LibDHCP {
|
class LibDHCP {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/// Map of factory functions.
|
||||||
|
typedef std::map<unsigned short, Option::Factory*> FactoryMap;
|
||||||
|
|
||||||
|
/// @brief Factory function to create instance of option.
|
||||||
|
///
|
||||||
|
/// Factory method creates instance of specified option. The option
|
||||||
|
/// to be created has to have corresponding factory function
|
||||||
|
/// registered with \ref LibDHCP::OptionFactoryRegister.
|
||||||
|
///
|
||||||
|
/// @param u universe of the option (V4 or V6)
|
||||||
|
/// @param type option-type
|
||||||
|
/// @param buf option-buffer
|
||||||
|
/// @throw isc::InvalidOperation if there is no factory function
|
||||||
|
/// registered for specified option type.
|
||||||
|
/// @return instance of option.
|
||||||
|
static isc::dhcp::OptionPtr optionFactory(isc::dhcp::Option::Universe u,
|
||||||
|
uint16_t type,
|
||||||
|
const OptionBuffer& buf);
|
||||||
|
|
||||||
/// Builds collection of options.
|
/// Builds collection of options.
|
||||||
///
|
///
|
||||||
/// Builds raw (on-wire) data for provided collection of options.
|
/// Builds raw (on-wire) data for provided collection of options.
|
||||||
@@ -84,10 +104,10 @@ public:
|
|||||||
Option::Factory * factory);
|
Option::Factory * factory);
|
||||||
protected:
|
protected:
|
||||||
/// pointers to factories that produce DHCPv6 options
|
/// pointers to factories that produce DHCPv6 options
|
||||||
static std::map<unsigned short, Option::Factory*> v4factories_;
|
static FactoryMap v4factories_;
|
||||||
|
|
||||||
/// pointers to factories that produce DHCPv6 options
|
/// pointers to factories that produce DHCPv6 options
|
||||||
static std::map<unsigned short, Option::Factory*> v6factories_;
|
static FactoryMap v6factories_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -29,6 +29,14 @@ using namespace isc::util;
|
|||||||
namespace isc {
|
namespace isc {
|
||||||
namespace dhcp {
|
namespace dhcp {
|
||||||
|
|
||||||
|
OptionPtr
|
||||||
|
Option::factory(Option::Universe u,
|
||||||
|
uint16_t type,
|
||||||
|
const OptionBuffer& buf) {
|
||||||
|
return(LibDHCP::optionFactory(u, type, buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Option::Option(Universe u, uint16_t type)
|
Option::Option(Universe u, uint16_t type)
|
||||||
:universe_(u), type_(type) {
|
:universe_(u), type_(type) {
|
||||||
|
|
||||||
|
@@ -63,9 +63,45 @@ public:
|
|||||||
/// @param type option type
|
/// @param type option type
|
||||||
/// @param buf pointer to a buffer
|
/// @param buf pointer to a buffer
|
||||||
///
|
///
|
||||||
|
/// @todo Passing a separate buffer for each option means that a copy
|
||||||
|
/// was done. We can avoid it by passing 2 iterators.
|
||||||
|
///
|
||||||
/// @return a pointer to a created option object
|
/// @return a pointer to a created option object
|
||||||
typedef OptionPtr Factory(Option::Universe u, uint16_t type, const OptionBuffer& buf);
|
typedef OptionPtr Factory(Option::Universe u, uint16_t type, const OptionBuffer& buf);
|
||||||
|
|
||||||
|
/// @brief Factory function to create instance of option.
|
||||||
|
///
|
||||||
|
/// Factory method creates instance of specified option. The option
|
||||||
|
/// to be created has to have corresponding factory function
|
||||||
|
/// registered with \ref LibDHCP::OptionFactoryRegister.
|
||||||
|
///
|
||||||
|
/// @param u universe of the option (V4 or V6)
|
||||||
|
/// @param type option-type
|
||||||
|
/// @param buf option-buffer
|
||||||
|
/// @throw isc::InvalidOperation if there is no factory function
|
||||||
|
/// registered for specified option type.
|
||||||
|
/// @return instance of option.
|
||||||
|
static OptionPtr factory(Option::Universe u,
|
||||||
|
uint16_t type,
|
||||||
|
const OptionBuffer& buf);
|
||||||
|
|
||||||
|
/// @brief Factory function to create instance of option.
|
||||||
|
///
|
||||||
|
/// Factory method creates instance of specified option. The option
|
||||||
|
/// to be created has to have corresponding factory function
|
||||||
|
/// registered with \ref LibDHCP::OptionFactoryRegister.
|
||||||
|
/// This method creates empty \ref OptionBuffer object. Use this
|
||||||
|
/// factory function if it is not needed to pass custom buffer.
|
||||||
|
///
|
||||||
|
/// @param u universe of the option (V4 or V6)
|
||||||
|
/// @param type option-type
|
||||||
|
/// @throw isc::InvalidOperation if there is no factory function
|
||||||
|
/// registered for specified option type.
|
||||||
|
/// @return instance of option.
|
||||||
|
static OptionPtr factory(Option::Universe u, uint16_t type) {
|
||||||
|
return factory(u, type, OptionBuffer());
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief ctor, used for options constructed, usually during transmission
|
/// @brief ctor, used for options constructed, usually during transmission
|
||||||
///
|
///
|
||||||
/// @param u option universe (DHCPv4 or DHCPv6)
|
/// @param u option universe (DHCPv4 or DHCPv6)
|
||||||
|
@@ -59,6 +59,7 @@ public:
|
|||||||
|
|
||||||
~IfaceMgrTest() {
|
~IfaceMgrTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// We need some known interface to work reliably. Loopback interface
|
// We need some known interface to work reliably. Loopback interface
|
||||||
@@ -217,6 +218,94 @@ TEST_F(IfaceMgrTest, getIface) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(IfaceMgrTest, multipleSockets) {
|
||||||
|
boost::scoped_ptr<NakedIfaceMgr> ifacemgr(new NakedIfaceMgr());
|
||||||
|
|
||||||
|
// container for initialized socket descriptors
|
||||||
|
std::list<uint16_t> init_sockets;
|
||||||
|
|
||||||
|
// create socket #1
|
||||||
|
int socket1 = 0;
|
||||||
|
ASSERT_NO_THROW(
|
||||||
|
socket1 = ifacemgr->openSocketFromIface(LOOPBACK, PORT1, AF_INET);
|
||||||
|
);
|
||||||
|
ASSERT_GT(socket1, 0);
|
||||||
|
init_sockets.push_back(socket1);
|
||||||
|
|
||||||
|
// create socket #2
|
||||||
|
IOAddress loAddr("127.0.0.1");
|
||||||
|
int socket2 = 0;
|
||||||
|
ASSERT_NO_THROW(
|
||||||
|
socket2 = ifacemgr->openSocketFromRemoteAddress(loAddr, PORT2);
|
||||||
|
);
|
||||||
|
ASSERT_GT(socket2, 0);
|
||||||
|
init_sockets.push_back(socket2);
|
||||||
|
|
||||||
|
// Get loopback interface. If we don't find one we are unable to run
|
||||||
|
// this test but we don't want to fail.
|
||||||
|
IfaceMgr::Iface* iface_ptr = ifacemgr->getIface(LOOPBACK);
|
||||||
|
if (iface_ptr == NULL) {
|
||||||
|
cout << "Local loopback interface not found. Skipping test. " << endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Once sockets have been sucessfully opened, they are supposed to
|
||||||
|
// be on the list. Here we start to test if all expected sockets
|
||||||
|
// are on the list and no other (unexpected) socket is there.
|
||||||
|
IfaceMgr::SocketCollection sockets = iface_ptr->getSockets();
|
||||||
|
int matched_sockets = 0;
|
||||||
|
for (std::list<uint16_t>::iterator init_sockets_it =
|
||||||
|
init_sockets.begin();
|
||||||
|
init_sockets_it != init_sockets.end(); ++init_sockets_it) {
|
||||||
|
// Set socket descriptors non blocking in order to be able
|
||||||
|
// to call recv() on them without hang.
|
||||||
|
int flags = fcntl(*init_sockets_it, F_GETFL, 0);
|
||||||
|
ASSERT_GE(flags, 0);
|
||||||
|
ASSERT_GE(fcntl(*init_sockets_it, F_SETFL, flags | O_NONBLOCK), 0);
|
||||||
|
// recv() is expected to result in EWOULDBLOCK error on non-blocking
|
||||||
|
// socket in case socket is valid but simply no data are coming in.
|
||||||
|
char buf;
|
||||||
|
recv(*init_sockets_it, &buf, 1, MSG_PEEK);
|
||||||
|
EXPECT_EQ(EWOULDBLOCK, errno);
|
||||||
|
// Apart from the ability to use the socket we want to make
|
||||||
|
// sure that socket on the list is the one that we created.
|
||||||
|
for (IfaceMgr::SocketCollection::const_iterator socket_it =
|
||||||
|
sockets.begin(); socket_it != sockets.end(); ++socket_it) {
|
||||||
|
if (*init_sockets_it == socket_it->sockfd_) {
|
||||||
|
// This socket is the one that we created.
|
||||||
|
++matched_sockets;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// all created sockets have been matched if this condition works.
|
||||||
|
EXPECT_EQ(sockets.size(), matched_sockets);
|
||||||
|
|
||||||
|
// closeSockets() is the other function that we want to test. It
|
||||||
|
// is supposed to close all sockets so as we will not be able to use
|
||||||
|
// them anymore communication.
|
||||||
|
ifacemgr->closeSockets();
|
||||||
|
|
||||||
|
// closed sockets are supposed to be removed from the list
|
||||||
|
sockets = iface_ptr->getSockets();
|
||||||
|
ASSERT_EQ(0, sockets.size());
|
||||||
|
|
||||||
|
// We are still in posession of socket descriptors that we created
|
||||||
|
// on the beginning of this test. We can use them to check whether
|
||||||
|
// closeSockets() only removed them from the list or they have been
|
||||||
|
// really closed.
|
||||||
|
for (std::list<uint16_t>::const_iterator init_sockets_it =
|
||||||
|
init_sockets.begin();
|
||||||
|
init_sockets_it != init_sockets.end(); ++init_sockets_it) {
|
||||||
|
// recv() must result in error when using invalid socket.
|
||||||
|
char buf;
|
||||||
|
recv(*init_sockets_it, &buf, 1, MSG_PEEK);
|
||||||
|
// EWOULDBLOCK would mean that socket is valid/open but
|
||||||
|
// simply no data is received so we have to check for
|
||||||
|
// other errors.
|
||||||
|
EXPECT_NE(EWOULDBLOCK, errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(IfaceMgrTest, sockets6) {
|
TEST_F(IfaceMgrTest, sockets6) {
|
||||||
// testing socket operation in a portable way is tricky
|
// testing socket operation in a portable way is tricky
|
||||||
// without interface detection implemented
|
// without interface detection implemented
|
||||||
@@ -317,6 +406,21 @@ TEST_F(IfaceMgrTest, socketsFromRemoteAddress) {
|
|||||||
);
|
);
|
||||||
EXPECT_GT(socket2, 0);
|
EXPECT_GT(socket2, 0);
|
||||||
close(socket2);
|
close(socket2);
|
||||||
|
|
||||||
|
// The following test is currently disabled for OSes other than
|
||||||
|
// Linux because interface detection is not implemented on them.
|
||||||
|
// @todo enable this test for all OSes once interface detection
|
||||||
|
// is implemented.
|
||||||
|
#if defined(OS_LINUX)
|
||||||
|
// Open v4 socket to connect to broadcast address.
|
||||||
|
int socket3 = 0;
|
||||||
|
IOAddress bcastAddr("255.255.255.255");
|
||||||
|
EXPECT_NO_THROW(
|
||||||
|
socket3 = ifacemgr->openSocketFromRemoteAddress(bcastAddr, PORT2);
|
||||||
|
);
|
||||||
|
EXPECT_GT(socket3, 0);
|
||||||
|
close(socket3);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: disabled due to other naming on various systems
|
// TODO: disabled due to other naming on various systems
|
||||||
|
@@ -18,6 +18,8 @@
|
|||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <util/buffer.h>
|
#include <util/buffer.h>
|
||||||
|
#include <dhcp/dhcp4.h>
|
||||||
|
#include <dhcp/dhcp6.h>
|
||||||
#include <dhcp/libdhcp++.h>
|
#include <dhcp/libdhcp++.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
@@ -31,6 +33,19 @@ class LibDhcpTest : public ::testing::Test {
|
|||||||
public:
|
public:
|
||||||
LibDhcpTest() {
|
LibDhcpTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Generic factory function to create any option.
|
||||||
|
///
|
||||||
|
/// Generic factory function to create any option.
|
||||||
|
///
|
||||||
|
/// @param u universe (V4 or V6)
|
||||||
|
/// @param type option-type
|
||||||
|
/// @param buf option-buffer
|
||||||
|
static OptionPtr genericOptionFactory(Option::Universe u, uint16_t type,
|
||||||
|
const OptionBuffer& buf) {
|
||||||
|
Option* option = new Option(u, type, buf);
|
||||||
|
return OptionPtr(option);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t packed[] = {
|
static const uint8_t packed[] = {
|
||||||
@@ -41,6 +56,78 @@ static const uint8_t packed[] = {
|
|||||||
1, 1, 0, 1, 114 // opt5 (5 bytes)
|
1, 1, 0, 1, 114 // opt5 (5 bytes)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TEST(LibDhcpTest, optionFactory) {
|
||||||
|
OptionBuffer buf;
|
||||||
|
// Factory functions for specific options must be registered before
|
||||||
|
// they can be used to create options instances. Otherwise exception
|
||||||
|
// is rised.
|
||||||
|
EXPECT_THROW(LibDHCP::optionFactory(Option::V4, DHO_SUBNET_MASK, buf),
|
||||||
|
isc::BadValue);
|
||||||
|
|
||||||
|
// Let's register some factory functions (two v4 and one v6 function).
|
||||||
|
// Registration may trigger exception if function for the specified
|
||||||
|
// option has been registered already.
|
||||||
|
ASSERT_NO_THROW(
|
||||||
|
LibDHCP::OptionFactoryRegister(Option::V4, DHO_SUBNET_MASK,
|
||||||
|
&LibDhcpTest::genericOptionFactory);
|
||||||
|
);
|
||||||
|
ASSERT_NO_THROW(
|
||||||
|
LibDHCP::OptionFactoryRegister(Option::V4, DHO_TIME_OFFSET,
|
||||||
|
&LibDhcpTest::genericOptionFactory);
|
||||||
|
);
|
||||||
|
ASSERT_NO_THROW(
|
||||||
|
LibDHCP::OptionFactoryRegister(Option::V6, D6O_CLIENTID,
|
||||||
|
&LibDhcpTest::genericOptionFactory);
|
||||||
|
);
|
||||||
|
|
||||||
|
// Invoke factory functions for all options (check if registration
|
||||||
|
// was successful).
|
||||||
|
OptionPtr opt_subnet_mask;
|
||||||
|
opt_subnet_mask = LibDHCP::optionFactory(Option::V4,
|
||||||
|
DHO_SUBNET_MASK,
|
||||||
|
buf);
|
||||||
|
// Check if non-NULL DHO_SUBNET_MASK option pointer has been returned.
|
||||||
|
ASSERT_TRUE(opt_subnet_mask);
|
||||||
|
// Validate if type and universe is correct.
|
||||||
|
EXPECT_EQ(Option::V4, opt_subnet_mask->getUniverse());
|
||||||
|
EXPECT_EQ(DHO_SUBNET_MASK, opt_subnet_mask->getType());
|
||||||
|
// Expect that option does not have content..
|
||||||
|
EXPECT_EQ(0, opt_subnet_mask->len() - opt_subnet_mask->getHeaderLen());
|
||||||
|
|
||||||
|
// Fill the time offset buffer with 4 bytes of data. Each byte set to 1.
|
||||||
|
OptionBuffer time_offset_buf(4, 1);
|
||||||
|
OptionPtr opt_time_offset;
|
||||||
|
opt_time_offset = LibDHCP::optionFactory(Option::V4,
|
||||||
|
DHO_TIME_OFFSET,
|
||||||
|
time_offset_buf);
|
||||||
|
// Check if non-NULL DHO_TIME_OFFSET option pointer has been returned.
|
||||||
|
ASSERT_TRUE(opt_time_offset);
|
||||||
|
// Validate if option length, type and universe is correct.
|
||||||
|
EXPECT_EQ(Option::V4, opt_time_offset->getUniverse());
|
||||||
|
EXPECT_EQ(DHO_TIME_OFFSET, opt_time_offset->getType());
|
||||||
|
EXPECT_EQ(time_offset_buf.size(),
|
||||||
|
opt_time_offset->len() - opt_time_offset->getHeaderLen());
|
||||||
|
// Validate data in the option.
|
||||||
|
EXPECT_TRUE(std::equal(time_offset_buf.begin(), time_offset_buf.end(),
|
||||||
|
opt_time_offset->getData().begin()));
|
||||||
|
|
||||||
|
// Fill the client id buffer with 20 bytes of data. Each byte set to 2.
|
||||||
|
OptionBuffer clientid_buf(20, 2);
|
||||||
|
OptionPtr opt_clientid;
|
||||||
|
opt_clientid = LibDHCP::optionFactory(Option::V6,
|
||||||
|
D6O_CLIENTID,
|
||||||
|
clientid_buf);
|
||||||
|
// Check if non-NULL D6O_CLIENTID option pointer has been returned.
|
||||||
|
ASSERT_TRUE(opt_clientid);
|
||||||
|
// Validate if option length, type and universe is correct.
|
||||||
|
EXPECT_EQ(Option::V6, opt_clientid->getUniverse());
|
||||||
|
EXPECT_EQ(D6O_CLIENTID, opt_clientid->getType());
|
||||||
|
EXPECT_EQ(clientid_buf.size(), opt_clientid->len() - opt_clientid->getHeaderLen());
|
||||||
|
// Validate data in the option.
|
||||||
|
EXPECT_TRUE(std::equal(clientid_buf.begin(), clientid_buf.end(),
|
||||||
|
opt_clientid->getData().begin()));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(LibDhcpTest, packOptions6) {
|
TEST(LibDhcpTest, packOptions6) {
|
||||||
OptionBuffer buf(512);
|
OptionBuffer buf(512);
|
||||||
isc::dhcp::Option::OptionCollection opts; // list of options
|
isc::dhcp::Option::OptionCollection opts; // list of options
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
SUBDIRS = . tests
|
SUBDIRS = . tests templates
|
||||||
|
|
||||||
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
|
AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
|
||||||
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
|
AM_CPPFLAGS += -I$(top_srcdir)/src/lib/log -I$(top_builddir)/src/lib/log
|
||||||
@@ -18,25 +18,27 @@ if USE_STATIC_LINK
|
|||||||
AM_LDFLAGS += -static
|
AM_LDFLAGS += -static
|
||||||
endif
|
endif
|
||||||
|
|
||||||
lib_LTLIBRARIES = libb10_perfdhcp++.la
|
pkglibexec_PROGRAMS = perfdhcp2
|
||||||
libb10_perfdhcp___la_SOURCES = command_options.cc command_options.h
|
perfdhcp2_SOURCES = main.cc
|
||||||
libb10_perfdhcp___la_SOURCES += localized_option.h
|
perfdhcp2_SOURCES += command_options.cc command_options.h
|
||||||
libb10_perfdhcp___la_SOURCES += perf_pkt6.cc perf_pkt6.h
|
perfdhcp2_SOURCES += localized_option.h
|
||||||
libb10_perfdhcp___la_SOURCES += perf_pkt4.cc perf_pkt4.h
|
perfdhcp2_SOURCES += perf_pkt6.cc perf_pkt6.h
|
||||||
libb10_perfdhcp___la_SOURCES += pkt_transform.cc pkt_transform.h
|
perfdhcp2_SOURCES += perf_pkt4.cc perf_pkt4.h
|
||||||
libb10_perfdhcp___la_SOURCES += stats_mgr.h
|
perfdhcp2_SOURCES += pkt_transform.cc pkt_transform.h
|
||||||
|
perfdhcp2_SOURCES += stats_mgr.h
|
||||||
|
perfdhcp2_SOURCES += test_control.cc test_control.h
|
||||||
libb10_perfdhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
|
libb10_perfdhcp___la_CXXFLAGS = $(AM_CXXFLAGS)
|
||||||
|
|
||||||
|
perfdhcp2_CXXFLAGS = $(AM_CXXFLAGS)
|
||||||
if USE_CLANGPP
|
if USE_CLANGPP
|
||||||
# Disable unused parameter warning caused by some of the
|
# Disable unused parameter warning caused by some of the
|
||||||
# Boost headers when compiling with clang.
|
# Boost headers when compiling with clang.
|
||||||
libb10_perfdhcp___la_CXXFLAGS += -Wno-unused-parameter
|
perfdhcp2_CXXFLAGS += -Wno-unused-parameter
|
||||||
endif
|
endif
|
||||||
|
|
||||||
libb10_perfdhcp___la_LIBADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
|
perfdhcp2_LDADD = $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
|
||||||
libb10_perfdhcp___la_LIBADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
|
perfdhcp2_LDADD += $(top_builddir)/src/lib/dhcp/libb10-dhcp++.la
|
||||||
libb10_perfdhcp___la_LIBADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
|
perfdhcp2_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
|
||||||
|
|
||||||
pkglibexec_PROGRAMS = perfdhcp
|
#pkglibexec_PROGRAMS = perfdhcp
|
||||||
perfdhcp_SOURCES = perfdhcp.c
|
#perfdhcp_SOURCES = perfdhcp.c
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
// PERFORMANCE OF THIS SOFTWARE.
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -20,9 +21,11 @@
|
|||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||||
|
|
||||||
#include "exceptions/exceptions.h"
|
#include <exceptions/exceptions.h>
|
||||||
|
#include <dhcp/dhcp6.h>
|
||||||
|
#include <dhcp/iface_mgr.h>
|
||||||
#include "command_options.h"
|
#include "command_options.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@@ -54,9 +57,10 @@ CommandOptions::reset() {
|
|||||||
rate_ = 0;
|
rate_ = 0;
|
||||||
report_delay_ = 0;
|
report_delay_ = 0;
|
||||||
clients_num_ = 0;
|
clients_num_ = 0;
|
||||||
mac_prefix_.assign(mac, mac + 6);
|
mac_template_.assign(mac, mac + 6);
|
||||||
base_.resize(0);
|
duid_template_.clear();
|
||||||
num_request_.resize(0);
|
base_.clear();
|
||||||
|
num_request_.clear();
|
||||||
period_ = 0;
|
period_ = 0;
|
||||||
drop_time_set_ = 0;
|
drop_time_set_ = 0;
|
||||||
drop_time_.assign(dt, dt + 2);
|
drop_time_.assign(dt, dt + 2);
|
||||||
@@ -81,6 +85,8 @@ CommandOptions::reset() {
|
|||||||
diags_.clear();
|
diags_.clear();
|
||||||
wrapped_.clear();
|
wrapped_.clear();
|
||||||
server_name_.clear();
|
server_name_.clear();
|
||||||
|
generateDuidTemplate();
|
||||||
|
commandline_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -127,9 +133,16 @@ CommandOptions::initialize(int argc, char** argv) {
|
|||||||
int offset_arg = 0; // Temporary variable holding offset arguments
|
int offset_arg = 0; // Temporary variable holding offset arguments
|
||||||
std::string sarg; // Temporary variable for string args
|
std::string sarg; // Temporary variable for string args
|
||||||
|
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << "perfdhcp";
|
||||||
|
|
||||||
// In this section we collect argument values from command line
|
// In this section we collect argument values from command line
|
||||||
// they will be tuned and validated elsewhere
|
// they will be tuned and validated elsewhere
|
||||||
while((opt = 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) {
|
while((opt = 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) {
|
||||||
|
stream << " -" << opt;
|
||||||
|
if (optarg) {
|
||||||
|
stream << " " << optarg;
|
||||||
|
}
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'v':
|
case 'v':
|
||||||
version();
|
version();
|
||||||
@@ -219,6 +232,7 @@ CommandOptions::initialize(int argc, char** argv) {
|
|||||||
|
|
||||||
case 'l':
|
case 'l':
|
||||||
localname_ = std::string(optarg);
|
localname_ = std::string(optarg);
|
||||||
|
initIsInterface();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
@@ -312,6 +326,8 @@ CommandOptions::initialize(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::cout << "Running: " << stream.str() << std::endl;
|
||||||
|
|
||||||
// If the IP version was not specified in the
|
// If the IP version was not specified in the
|
||||||
// command line, assume IPv4.
|
// command line, assume IPv4.
|
||||||
if (ipversion_ == 0) {
|
if (ipversion_ == 0) {
|
||||||
@@ -351,7 +367,27 @@ CommandOptions::initialize(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO handle -l option with IfaceManager when it is created
|
// Handle the local '-l' address/interface
|
||||||
|
if (!localname_.empty()) {
|
||||||
|
if (server_name_.empty()) {
|
||||||
|
if (is_interface_ && (ipversion_ == 4)) {
|
||||||
|
broadcast_ = 1;
|
||||||
|
server_name_ = "255.255.255.255";
|
||||||
|
} else if (is_interface_ && (ipversion_ == 6)) {
|
||||||
|
server_name_ = "FF02::1:2";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (server_name_.empty()) {
|
||||||
|
isc_throw(InvalidParameter,
|
||||||
|
"without an inteface server is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If DUID is not specified from command line we need to
|
||||||
|
// generate one.
|
||||||
|
if (duid_template_.size() == 0) {
|
||||||
|
generateDuidTemplate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -376,6 +412,17 @@ CommandOptions::initClientsNum() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CommandOptions::initIsInterface() {
|
||||||
|
is_interface_ = false;
|
||||||
|
if (!localname_.empty()) {
|
||||||
|
dhcp::IfaceMgr& iface_mgr = dhcp::IfaceMgr::instance();
|
||||||
|
if (iface_mgr.getIface(localname_) != NULL) {
|
||||||
|
is_interface_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CommandOptions::decodeBase(const std::string& base) {
|
CommandOptions::decodeBase(const std::string& base) {
|
||||||
std::string b(base);
|
std::string b(base);
|
||||||
@@ -402,7 +449,7 @@ CommandOptions::decodeMac(const std::string& base) {
|
|||||||
// Decode mac address to vector of uint8_t
|
// Decode mac address to vector of uint8_t
|
||||||
std::istringstream s1(base.substr(found + 1));
|
std::istringstream s1(base.substr(found + 1));
|
||||||
std::string token;
|
std::string token;
|
||||||
mac_prefix_.clear();
|
mac_template_.clear();
|
||||||
// Get pieces of MAC address separated with : (or even ::)
|
// Get pieces of MAC address separated with : (or even ::)
|
||||||
while (std::getline(s1, token, ':')) {
|
while (std::getline(s1, token, ':')) {
|
||||||
unsigned int ui = 0;
|
unsigned int ui = 0;
|
||||||
@@ -417,16 +464,17 @@ CommandOptions::decodeMac(const std::string& base) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
// If conversion succeeded store byte value
|
// If conversion succeeded store byte value
|
||||||
mac_prefix_.push_back(ui);
|
mac_template_.push_back(ui);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// MAC address must consist of 6 octets, otherwise it is invalid
|
// MAC address must consist of 6 octets, otherwise it is invalid
|
||||||
check(mac_prefix_.size() != 6, errmsg);
|
check(mac_template_.size() != 6, errmsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CommandOptions::decodeDuid(const std::string& base) {
|
CommandOptions::decodeDuid(const std::string& base) {
|
||||||
// Strip argument from duid=
|
// Strip argument from duid=
|
||||||
|
std::vector<uint8_t> duid_template;
|
||||||
size_t found = base.find('=');
|
size_t found = base.find('=');
|
||||||
check(found == std::string::npos, "expected -b<base> format for duid is -b duid=<duid>");
|
check(found == std::string::npos, "expected -b<base> format for duid is -b duid=<duid>");
|
||||||
std::string b = base.substr(found + 1);
|
std::string b = base.substr(found + 1);
|
||||||
@@ -446,8 +494,44 @@ CommandOptions::decodeDuid(const std::string& base) {
|
|||||||
isc_throw(isc::InvalidParameter,
|
isc_throw(isc::InvalidParameter,
|
||||||
"invalid characters in DUID provided, exepected hex digits");
|
"invalid characters in DUID provided, exepected hex digits");
|
||||||
}
|
}
|
||||||
duid_prefix_.push_back(static_cast<uint8_t>(ui));
|
duid_template.push_back(static_cast<uint8_t>(ui));
|
||||||
}
|
}
|
||||||
|
// @todo Get rid of this limitation when we manage add support
|
||||||
|
// for DUIDs other than LLT. Shorter DUIDs may be useful for
|
||||||
|
// server testing purposes.
|
||||||
|
check(duid_template.size() < 6, "DUID must be at least 6 octets long");
|
||||||
|
// Assign the new duid only if successfully generated.
|
||||||
|
std::swap(duid_template, duid_template_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CommandOptions::generateDuidTemplate() {
|
||||||
|
using namespace boost::posix_time;
|
||||||
|
// Duid template will be most likely generated only once but
|
||||||
|
// it is ok if it is called more then once so we simply
|
||||||
|
// regenerate it and discard previous value.
|
||||||
|
duid_template_.clear();
|
||||||
|
const uint8_t duid_template_len = 14;
|
||||||
|
duid_template_.resize(duid_template_len);
|
||||||
|
// The first four octets consist of DUID LLT and hardware type.
|
||||||
|
duid_template_[0] = DUID_LLT >> 8;
|
||||||
|
duid_template_[1] = DUID_LLT & 0xff;
|
||||||
|
duid_template_[2] = HWTYPE_ETHERNET >> 8;
|
||||||
|
duid_template_[3] = HWTYPE_ETHERNET & 0xff;
|
||||||
|
|
||||||
|
// As described in RFC3315: 'the time value is the time
|
||||||
|
// that the DUID is generated represented in seconds
|
||||||
|
// since midnight (UTC), January 1, 2000, modulo 2^32.'
|
||||||
|
ptime now = microsec_clock::universal_time();
|
||||||
|
ptime duid_epoch(from_iso_string("20000101T000000"));
|
||||||
|
time_period period(duid_epoch, now);
|
||||||
|
uint32_t duration_sec = htonl(period.length().total_seconds());
|
||||||
|
memcpy(&duid_template_[4], &duration_sec, 4);
|
||||||
|
|
||||||
|
// Set link layer address (6 octets). This value may be
|
||||||
|
// randomized before sending a packet to simulate different
|
||||||
|
// clients.
|
||||||
|
memcpy(&duid_template_[8], &mac_template_[0], 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t
|
uint8_t
|
||||||
@@ -564,6 +648,98 @@ CommandOptions::nonEmptyString(const std::string& errmsg) const {
|
|||||||
return sarg;
|
return sarg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CommandOptions::printCommandLine() const {
|
||||||
|
std::cout << "IPv" << static_cast<int>(ipversion_) << std::endl;
|
||||||
|
if (exchange_mode_ == DO_SA) {
|
||||||
|
if (ipversion_ == 4) {
|
||||||
|
std::cout << "DISCOVER-OFFER only" << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "SOLICIT-ADVERETISE only" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rate_ != 0) {
|
||||||
|
std::cout << "rate[1/s]=" << rate_ << std::endl;
|
||||||
|
}
|
||||||
|
if (report_delay_ != 0) {
|
||||||
|
std::cout << "report[s]=" << report_delay_ << std::endl;
|
||||||
|
}
|
||||||
|
if (clients_num_ != 0) {
|
||||||
|
std::cout << "clients=" << clients_num_ << std::endl;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < base_.size(); ++i) {
|
||||||
|
std::cout << "base[" << i << "]=" << base_[i] << std::endl;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < num_request_.size(); ++i) {
|
||||||
|
std::cout << "num-request[" << i << "]=" << num_request_[i] << std::endl;
|
||||||
|
}
|
||||||
|
if (period_ != 0) {
|
||||||
|
std::cout << "test-period=" << period_ << std::endl;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < drop_time_.size(); ++i) {
|
||||||
|
std::cout << "drop-time[" << i << "]=" << drop_time_[i] << std::endl;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < max_drop_.size(); ++i) {
|
||||||
|
std::cout << "max-drop{" << i << "]=" << max_drop_[i] << std::endl;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < max_pdrop_.size(); ++i) {
|
||||||
|
std::cout << "max-pdrop{" << i << "]=" << max_pdrop_[i] << std::endl;
|
||||||
|
}
|
||||||
|
if (preload_ != 0) {
|
||||||
|
std::cout << "preload=" << preload_ << std::endl;
|
||||||
|
}
|
||||||
|
std::cout << "aggressivity=" << aggressivity_ << std::endl;
|
||||||
|
if (getLocalPort() != 0) {
|
||||||
|
std::cout << "local-port=" << local_port_ << std::endl;
|
||||||
|
}
|
||||||
|
if (seeded_) {
|
||||||
|
std::cout << "seed=" << seed_ << std::endl;
|
||||||
|
}
|
||||||
|
if (broadcast_) {
|
||||||
|
std::cout << "broadcast" << std::endl;
|
||||||
|
}
|
||||||
|
if (rapid_commit_) {
|
||||||
|
std::cout << "rapid-commit" << std::endl;
|
||||||
|
}
|
||||||
|
if (use_first_) {
|
||||||
|
std::cout << "use-first" << std::endl;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < template_file_.size(); ++i) {
|
||||||
|
std::cout << "template-file[" << i << "]=" << template_file_[i] << std::endl;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < xid_offset_.size(); ++i) {
|
||||||
|
std::cout << "xid-offset[" << i << "]=" << xid_offset_[i] << std::endl;
|
||||||
|
}
|
||||||
|
if (elp_offset_ != 0) {
|
||||||
|
std::cout << "elp-offset=" << elp_offset_ << std::endl;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < rnd_offset_.size(); ++i) {
|
||||||
|
std::cout << "rnd-offset[" << i << "]=" << rnd_offset_[i] << std::endl;
|
||||||
|
}
|
||||||
|
if (sid_offset_ != 0) {
|
||||||
|
std::cout << "sid-offset=" << sid_offset_ << std::endl;
|
||||||
|
}
|
||||||
|
if (rip_offset_ != 0) {
|
||||||
|
std::cout << "rip-offset=" << rip_offset_ << std::endl;
|
||||||
|
}
|
||||||
|
if (!diags_.empty()) {
|
||||||
|
std::cout << "diagnostic-selectors=" << diags_ << std::endl;
|
||||||
|
}
|
||||||
|
if (!wrapped_.empty()) {
|
||||||
|
std::cout << "wrapped=" << wrapped_ << std::endl;
|
||||||
|
}
|
||||||
|
if (!localname_.empty()) {
|
||||||
|
if (is_interface_) {
|
||||||
|
std::cout << "interface=" << localname_ << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cout << "local-addr=" << localname_ << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!server_name_.empty()) {
|
||||||
|
std::cout << "server=" << server_name_ << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CommandOptions::usage() const {
|
CommandOptions::usage() const {
|
||||||
fprintf(stdout, "%s",
|
fprintf(stdout, "%s",
|
||||||
@@ -691,7 +867,7 @@ CommandOptions::usage() const {
|
|||||||
|
|
||||||
void
|
void
|
||||||
CommandOptions::version() const {
|
CommandOptions::version() const {
|
||||||
fprintf(stdout, "version 0.01\n");
|
std::cout << "VERSION: " << VERSION << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -23,7 +23,7 @@
|
|||||||
namespace isc {
|
namespace isc {
|
||||||
namespace perfdhcp {
|
namespace perfdhcp {
|
||||||
|
|
||||||
/// \brief Command Options
|
/// \brief Command Options.
|
||||||
///
|
///
|
||||||
/// This class is responsible for parsing the command-line and storing the
|
/// This class is responsible for parsing the command-line and storing the
|
||||||
/// specified options.
|
/// specified options.
|
||||||
@@ -49,64 +49,64 @@ public:
|
|||||||
/// command line options.
|
/// command line options.
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
/// \brief Parse command line
|
/// \brief Parse command line.
|
||||||
///
|
///
|
||||||
/// Parses the command line and stores the selected options
|
/// Parses the command line and stores the selected options
|
||||||
/// in class data members.
|
/// in class data members.
|
||||||
///
|
///
|
||||||
/// \param argc Argument count passed to main().
|
/// \param argc Argument count passed to main().
|
||||||
/// \param argv Argument value array passed to main().
|
/// \param argv Argument value array passed to main().
|
||||||
/// \throws isc::InvalidParameter if parse fails
|
/// \throws isc::InvalidParameter if parse fails.
|
||||||
void parse(int argc, char** const argv);
|
void parse(int argc, char** const argv);
|
||||||
|
|
||||||
/// \brief Returns IP version
|
/// \brief Returns IP version.
|
||||||
///
|
///
|
||||||
/// \return IP version to be used
|
/// \return IP version to be used.
|
||||||
uint8_t getIpVersion() const { return ipversion_; }
|
uint8_t getIpVersion() const { return ipversion_; }
|
||||||
|
|
||||||
/// \brief Returns packet exchange mode
|
/// \brief Returns packet exchange mode.
|
||||||
///
|
///
|
||||||
/// \return packet exchange mode
|
/// \return packet exchange mode.
|
||||||
ExchangeMode getExchangeMode() const { return exchange_mode_; }
|
ExchangeMode getExchangeMode() const { return exchange_mode_; }
|
||||||
|
|
||||||
/// \brief Returns echange rate
|
/// \brief Returns echange rate.
|
||||||
///
|
///
|
||||||
/// \return exchange rate per second
|
/// \return exchange rate per second.
|
||||||
int getRate() const { return rate_; }
|
int getRate() const { return rate_; }
|
||||||
|
|
||||||
/// \brief Returns delay between two performance reports
|
/// \brief Returns delay between two performance reports.
|
||||||
///
|
///
|
||||||
/// \return delay between two consecutive performance reports
|
/// \return delay between two consecutive performance reports.
|
||||||
int getReportDelay() const { return report_delay_; }
|
int getReportDelay() const { return report_delay_; }
|
||||||
|
|
||||||
/// \brief Returns number of simulated clients
|
/// \brief Returns number of simulated clients.
|
||||||
///
|
///
|
||||||
/// \return number of simulated clients
|
/// \return number of simulated clients.
|
||||||
uint32_t getClientsNum() const { return clients_num_; }
|
uint32_t getClientsNum() const { return clients_num_; }
|
||||||
|
|
||||||
/// \brief Returns MAC address prefix
|
/// \brief Returns MAC address template.
|
||||||
///
|
///
|
||||||
/// \ return MAC address prefix to simulate different clients
|
/// \return MAC address template to simulate different clients.
|
||||||
std::vector<uint8_t> getMacPrefix() const { return mac_prefix_; }
|
std::vector<uint8_t> getMacTemplate() const { return mac_template_; }
|
||||||
|
|
||||||
/// \brief Returns DUID prefix
|
/// \brief Returns DUID template.
|
||||||
///
|
///
|
||||||
/// \return DUID prefix to simulate different clients
|
/// \return DUID template to simulate different clients.
|
||||||
std::vector<uint8_t> getDuidPrefix() const { return duid_prefix_; }
|
std::vector<uint8_t> getDuidTemplate() const { return duid_template_; }
|
||||||
|
|
||||||
/// \brief Returns base values
|
/// \brief Returns base values.
|
||||||
///
|
///
|
||||||
/// \return all base values specified
|
/// \return all base values specified.
|
||||||
std::vector<std::string> getBase() const { return base_; }
|
std::vector<std::string> getBase() const { return base_; }
|
||||||
|
|
||||||
/// \brief Returns maximum number of exchanges
|
/// \brief Returns maximum number of exchanges.
|
||||||
///
|
///
|
||||||
/// \return number of exchange requests before test is aborted
|
/// \return number of exchange requests before test is aborted.
|
||||||
std::vector<int> getNumRequests() const { return num_request_; }
|
std::vector<int> getNumRequests() const { return num_request_; }
|
||||||
|
|
||||||
/// \brief Returns test period
|
/// \brief Returns test period.
|
||||||
///
|
///
|
||||||
/// \return test period before it is aborted
|
/// \return test period before it is aborted.
|
||||||
int getPeriod() const { return period_; }
|
int getPeriod() const { return period_; }
|
||||||
|
|
||||||
/// \brief Returns drop time
|
/// \brief Returns drop time
|
||||||
@@ -114,136 +114,139 @@ public:
|
|||||||
/// The method returns maximum time elapsed from
|
/// The method returns maximum time elapsed from
|
||||||
/// sending the packet before it is assumed dropped.
|
/// sending the packet before it is assumed dropped.
|
||||||
///
|
///
|
||||||
/// \return return time before request is assumed dropped
|
/// \return return time before request is assumed dropped.
|
||||||
std::vector<double> getDropTime() const { return drop_time_; }
|
std::vector<double> getDropTime() const { return drop_time_; }
|
||||||
|
|
||||||
/// \brief Returns maximum drops number
|
/// \brief Returns maximum drops number.
|
||||||
///
|
///
|
||||||
/// Returns maximum number of packet drops before
|
/// Returns maximum number of packet drops before
|
||||||
/// aborting a test.
|
/// aborting a test.
|
||||||
///
|
///
|
||||||
/// \return maximum number of dropped requests
|
/// \return maximum number of dropped requests.
|
||||||
std::vector<int> getMaxDrop() const { return max_drop_; }
|
std::vector<int> getMaxDrop() const { return max_drop_; }
|
||||||
|
|
||||||
/// \brief Returns maximal percentage of drops
|
/// \brief Returns maximal percentage of drops.
|
||||||
///
|
///
|
||||||
/// Returns maximal percentage of packet drops
|
/// Returns maximal percentage of packet drops
|
||||||
/// before aborting a test.
|
/// before aborting a test.
|
||||||
///
|
///
|
||||||
/// \return maximum percentage of lost requests
|
/// \return maximum percentage of lost requests.
|
||||||
std::vector<double> getMaxDropPercentage() const { return max_pdrop_; }
|
std::vector<double> getMaxDropPercentage() const { return max_pdrop_; }
|
||||||
|
|
||||||
/// \brief Returns local address or interface name
|
/// \brief Returns local address or interface name.
|
||||||
///
|
///
|
||||||
/// \return local address or interface name
|
/// \return local address or interface name.
|
||||||
std::string getLocalName() const { return localname_; }
|
std::string getLocalName() const { return localname_; }
|
||||||
|
|
||||||
/// \brief Checks if interface name was used
|
/// \brief Checks if interface name was used.
|
||||||
///
|
///
|
||||||
/// The method checks if interface name was used
|
/// The method checks if interface name was used
|
||||||
/// rather than address.
|
/// rather than address.
|
||||||
///
|
///
|
||||||
/// \return true if interface name was used
|
/// \return true if interface name was used.
|
||||||
bool isInterface() const { return is_interface_; }
|
bool isInterface() const { return is_interface_; }
|
||||||
|
|
||||||
/// \brief Returns number of preload exchanges
|
/// \brief Returns number of preload exchanges.
|
||||||
///
|
///
|
||||||
/// \return number of preload exchanges
|
/// \return number of preload exchanges.
|
||||||
int getPreload() const { return preload_; }
|
int getPreload() const { return preload_; }
|
||||||
|
|
||||||
/// \brief Returns aggressivity value
|
/// \brief Returns aggressivity value.
|
||||||
///
|
///
|
||||||
/// \return aggressivity value
|
/// \return aggressivity value.
|
||||||
int getAggressivity() const { return aggressivity_; }
|
int getAggressivity() const { return aggressivity_; }
|
||||||
|
|
||||||
/// \brief Returns local port number
|
/// \brief Returns local port number.
|
||||||
///
|
///
|
||||||
/// \return local port number
|
/// \return local port number.
|
||||||
int getLocalPort() const { return local_port_; }
|
int getLocalPort() const { return local_port_; }
|
||||||
|
|
||||||
/// \brief Checks if seed provided
|
/// \brief Checks if seed provided.
|
||||||
///
|
///
|
||||||
/// \return true if seed was provided
|
/// \return true if seed was provided.
|
||||||
bool isSeeded() const { return seeded_; }
|
bool isSeeded() const { return seeded_; }
|
||||||
|
|
||||||
/// \brief Returns radom seed
|
/// \brief Returns radom seed.
|
||||||
///
|
///
|
||||||
/// \return random seed
|
/// \return random seed.
|
||||||
uint32_t getSeed() const { return seed_; }
|
uint32_t getSeed() const { return seed_; }
|
||||||
|
|
||||||
/// \brief Checks if broadcast address is to be used
|
/// \brief Checks if broadcast address is to be used.
|
||||||
///
|
///
|
||||||
/// \return true if broadcast address is to be used
|
/// \return true if broadcast address is to be used.
|
||||||
bool isBroadcast() const { return broadcast_; }
|
bool isBroadcast() const { return broadcast_; }
|
||||||
|
|
||||||
/// \brief Check if rapid commit option used
|
/// \brief Check if rapid commit option used.
|
||||||
///
|
///
|
||||||
/// \return true if rapid commit option is used
|
/// \return true if rapid commit option is used.
|
||||||
bool isRapidCommit() const { return rapid_commit_; }
|
bool isRapidCommit() const { return rapid_commit_; }
|
||||||
|
|
||||||
/// \brief Check if server-ID to be taken from first package
|
/// \brief Check if server-ID to be taken from first package.
|
||||||
///
|
///
|
||||||
/// \return true if server-iD to be taken from first package
|
/// \return true if server-iD to be taken from first package.
|
||||||
bool isUseFirst() const { return use_first_; }
|
bool isUseFirst() const { return use_first_; }
|
||||||
|
|
||||||
/// \brief Returns template file names
|
/// \brief Returns template file names.
|
||||||
///
|
///
|
||||||
/// \return template file names
|
/// \return template file names.
|
||||||
std::vector<std::string> getTemplateFiles() const { return template_file_; }
|
std::vector<std::string> getTemplateFiles() const { return template_file_; }
|
||||||
|
|
||||||
/// brief Returns template offsets for xid
|
/// brief Returns template offsets for xid.
|
||||||
///
|
///
|
||||||
/// \return template offsets for xid
|
/// \return template offsets for xid.
|
||||||
std::vector<int> getTransactionIdOffset() const { return xid_offset_; }
|
std::vector<int> getTransactionIdOffset() const { return xid_offset_; }
|
||||||
|
|
||||||
/// \brief Returns template offsets for rnd
|
/// \brief Returns template offsets for rnd.
|
||||||
///
|
///
|
||||||
/// \return template offsets for rnd
|
/// \return template offsets for rnd.
|
||||||
std::vector<int> getRandomOffset() const { return rnd_offset_; }
|
std::vector<int> getRandomOffset() const { return rnd_offset_; }
|
||||||
|
|
||||||
/// \brief Returns template offset for elapsed time
|
/// \brief Returns template offset for elapsed time.
|
||||||
///
|
///
|
||||||
/// \return template offset for elapsed time
|
/// \return template offset for elapsed time.
|
||||||
int getElapsedTimeOffset() const { return elp_offset_; }
|
int getElapsedTimeOffset() const { return elp_offset_; }
|
||||||
|
|
||||||
/// \brief Returns template offset for server-ID
|
/// \brief Returns template offset for server-ID.
|
||||||
///
|
///
|
||||||
/// \return template offset for server-ID
|
/// \return template offset for server-ID.
|
||||||
int getServerIdOffset() const { return sid_offset_; }
|
int getServerIdOffset() const { return sid_offset_; }
|
||||||
|
|
||||||
/// \brief Returns template offset for requested IP
|
/// \brief Returns template offset for requested IP.
|
||||||
///
|
///
|
||||||
/// \return template offset for requested IP
|
/// \return template offset for requested IP.
|
||||||
int getRequestedIpOffset() const { return rip_offset_; }
|
int getRequestedIpOffset() const { return rip_offset_; }
|
||||||
|
|
||||||
/// \brief Returns diagnostic selectors
|
/// \brief Returns diagnostic selectors.
|
||||||
///
|
///
|
||||||
/// \return diagnostics selector
|
/// \return diagnostics selector.
|
||||||
std::string getDiags() const { return diags_; }
|
std::string getDiags() const { return diags_; }
|
||||||
|
|
||||||
/// \brief Returns wrapped command
|
/// \brief Returns wrapped command.
|
||||||
///
|
///
|
||||||
/// \return wrapped command (start/stop)
|
/// \return wrapped command (start/stop).
|
||||||
std::string getWrapped() const { return wrapped_; }
|
std::string getWrapped() const { return wrapped_; }
|
||||||
|
|
||||||
/// \brief Returns server name
|
/// \brief Returns server name.
|
||||||
///
|
///
|
||||||
/// \return server name
|
/// \return server name.
|
||||||
std::string getServerName() const { return server_name_; }
|
std::string getServerName() const { return server_name_; }
|
||||||
|
|
||||||
|
/// \brief Print command line arguments.
|
||||||
|
void printCommandLine() const;
|
||||||
|
|
||||||
/// \brief Print usage
|
/// \brief Print usage.
|
||||||
///
|
///
|
||||||
/// Prints perfdhcp usage
|
/// Prints perfdhcp usage.
|
||||||
void usage() const;
|
void usage() const;
|
||||||
|
|
||||||
/// \brief Print program version
|
/// \brief Print program version.
|
||||||
///
|
///
|
||||||
/// Prints perfdhcp version
|
/// Prints perfdhcp version.
|
||||||
void version() const;
|
void version() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/// \brief Default Constructor
|
/// \brief Default Constructor.
|
||||||
///
|
///
|
||||||
/// Private constructor as this is a singleton class.
|
/// Private constructor as this is a singleton class.
|
||||||
/// Use CommandOptions::instance() to get instance of it.
|
/// Use CommandOptions::instance() to get instance of it.
|
||||||
@@ -251,57 +254,64 @@ private:
|
|||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Initializes class members based command line
|
/// \brief Initializes class members based on the command line.
|
||||||
///
|
///
|
||||||
/// Reads each command line parameter and sets class member values
|
/// Reads each command line parameter and sets class member values.
|
||||||
///
|
///
|
||||||
/// \param argc Argument count passed to main().
|
/// \param argc Argument count passed to main().
|
||||||
/// \param argv Argument value array passed to main().
|
/// \param argv Argument value array passed to main().
|
||||||
/// \throws isc::InvalidParameter if command line options initialization fails
|
/// \throws isc::InvalidParameter if command line options initialization fails.
|
||||||
void initialize(int argc, char** argv);
|
void initialize(int argc, char** argv);
|
||||||
|
|
||||||
/// \brief Validates initialized options
|
/// \brief Validates initialized options.
|
||||||
///
|
///
|
||||||
/// \throws isc::InvalidParameter if command line validation fails
|
/// \throws isc::InvalidParameter if command line validation fails.
|
||||||
void validate() const;
|
void validate() const;
|
||||||
|
|
||||||
/// \brief Throws !InvalidParameter exception if condition is true
|
/// \brief Throws !InvalidParameter exception if condition is true.
|
||||||
///
|
///
|
||||||
/// Convenience function that throws an InvalidParameter exception if
|
/// Convenience function that throws an InvalidParameter exception if
|
||||||
/// the condition argument is true
|
/// the condition argument is true.
|
||||||
///
|
///
|
||||||
/// \param condition Condition to be checked
|
/// \param condition Condition to be checked.
|
||||||
/// \param errmsg Error message in exception
|
/// \param errmsg Error message in exception.
|
||||||
/// \throws isc::InvalidParameter if condition argument true
|
/// \throws isc::InvalidParameter if condition argument true.
|
||||||
inline void check(bool condition, const std::string& errmsg) const;
|
inline void check(bool condition, const std::string& errmsg) const;
|
||||||
|
|
||||||
/// \brief Casts command line argument to positive integer
|
/// \brief Casts command line argument to positive integer.
|
||||||
///
|
///
|
||||||
/// \param errmsg Error message if lexical cast fails
|
/// \param errmsg Error message if lexical cast fails.
|
||||||
/// \throw InvalidParameter if lexical cast fails
|
/// \throw InvalidParameter if lexical cast fails.
|
||||||
int positiveInteger(const std::string& errmsg) const;
|
int positiveInteger(const std::string& errmsg) const;
|
||||||
|
|
||||||
/// \brief Casts command line argument to non-negative integer
|
/// \brief Casts command line argument to non-negative integer.
|
||||||
///
|
///
|
||||||
/// \param errmsg Error message if lexical cast fails
|
/// \param errmsg Error message if lexical cast fails.
|
||||||
/// \throw InvalidParameter if lexical cast fails
|
/// \throw InvalidParameter if lexical cast fails.
|
||||||
int nonNegativeInteger(const std::string& errmsg) const;
|
int nonNegativeInteger(const std::string& errmsg) const;
|
||||||
|
|
||||||
/// \brief Returns command line string if it is not empty
|
/// \brief Returns command line string if it is not empty.
|
||||||
///
|
///
|
||||||
/// \param errmsg Error message if string is empty
|
/// \param errmsg Error message if string is empty.
|
||||||
/// \throw InvalidParameter if string is empty
|
/// \throw InvalidParameter if string is empty.
|
||||||
std::string nonEmptyString(const std::string& errmsg) const;
|
std::string nonEmptyString(const std::string& errmsg) const;
|
||||||
|
|
||||||
/// \brief Set number of clients
|
/// \brief Set number of clients.
|
||||||
///
|
///
|
||||||
/// Interprets the getopt() "opt" global variable as the number of clients
|
/// Interprets the getopt() "opt" global variable as the number of clients
|
||||||
/// (a non-negative number). This value is specified by the "-R" switch.
|
/// (a non-negative number). This value is specified by the "-R" switch.
|
||||||
///
|
///
|
||||||
/// \throw InvalidParameter if -R<value> is wrong
|
/// \throw InvalidParameter if -R<value> is wrong.
|
||||||
void initClientsNum();
|
void initClientsNum();
|
||||||
|
|
||||||
/// \brief Decodes base provided with -b<base>
|
/// \brief Sets value indicating if interface name was given.
|
||||||
|
///
|
||||||
|
/// Method checks if the command line argument given with
|
||||||
|
/// '-l' option is the interface name. The is_interface_ member
|
||||||
|
/// is set accordingly.
|
||||||
|
void initIsInterface();
|
||||||
|
|
||||||
|
/// \brief Decodes base provided with -b<base>.
|
||||||
///
|
///
|
||||||
/// Function decodes argument of -b switch, which
|
/// Function decodes argument of -b switch, which
|
||||||
/// specifies a base value used to generate unique
|
/// specifies a base value used to generate unique
|
||||||
@@ -311,39 +321,47 @@ private:
|
|||||||
/// - -b mac=00:01:02:03:04:05
|
/// - -b mac=00:01:02:03:04:05
|
||||||
/// - -b duid=0F1234 (duid can be up to 128 hex digits)
|
/// - -b duid=0F1234 (duid can be up to 128 hex digits)
|
||||||
// Function will decode 00:01:02:03:04:05 and/or
|
// Function will decode 00:01:02:03:04:05 and/or
|
||||||
/// 0F1234 respectively and initialize mac_prefix_
|
/// 0F1234 respectively and initialize mac_template_
|
||||||
/// and/or duid_prefix_ members
|
/// and/or duid_template_ members.
|
||||||
///
|
///
|
||||||
/// \param base Base in string format
|
/// \param base Base in string format.
|
||||||
/// \throws isc::InvalidParameter if base is invalid
|
/// \throws isc::InvalidParameter if base is invalid.
|
||||||
void decodeBase(const std::string& base);
|
void decodeBase(const std::string& base);
|
||||||
|
|
||||||
/// \brief Decodes base MAC address provided with -b<base>
|
/// \brief Decodes base MAC address provided with -b<base>.
|
||||||
///
|
///
|
||||||
/// Function decodes parameter given as -b mac=00:01:02:03:04:05
|
/// Function decodes parameter given as -b mac=00:01:02:03:04:05
|
||||||
/// The function will decode 00:01:02:03:04:05 initialize mac_prefix_
|
/// The function will decode 00:01:02:03:04:05 initialize mac_template_
|
||||||
/// class member.
|
/// class member.
|
||||||
/// Provided MAC address is for example only
|
/// Provided MAC address is for example only.
|
||||||
///
|
///
|
||||||
/// \param base Base string given as -b mac=00:01:02:03:04:05
|
/// \param base Base string given as -b mac=00:01:02:03:04:05.
|
||||||
/// \throws isc::InvalidParameter if mac address is invalid
|
/// \throws isc::InvalidParameter if mac address is invalid.
|
||||||
void decodeMac(const std::string& base);
|
void decodeMac(const std::string& base);
|
||||||
|
|
||||||
/// \brief Decodes base DUID provided with -b<base>
|
/// \brief Decodes base DUID provided with -b<base>.
|
||||||
///
|
///
|
||||||
/// Function decodes parameter given as -b duid=0F1234
|
/// Function decodes parameter given as -b duid=0F1234.
|
||||||
/// The function will decode 0F1234 and initialize duid_prefix_
|
/// The function will decode 0F1234 and initialize duid_template_
|
||||||
/// class member.
|
/// class member.
|
||||||
/// Provided DUID is for example only.
|
/// Provided DUID is for example only.
|
||||||
///
|
///
|
||||||
/// \param base Base string given as -b duid=0F1234
|
/// \param base Base string given as -b duid=0F1234.
|
||||||
/// \throws isc::InvalidParameter if DUID is invalid
|
/// \throws isc::InvalidParameter if DUID is invalid.
|
||||||
void decodeDuid(const std::string& base);
|
void decodeDuid(const std::string& base);
|
||||||
|
|
||||||
/// \brief Converts two-digit hexadecimal string to a byte
|
/// \brief Generates DUID-LLT (based on link layer address).
|
||||||
///
|
///
|
||||||
/// \param hex_text Hexadecimal string e.g. AF
|
/// Function generates DUID based on link layer address and
|
||||||
/// \throw isc::InvalidParameter if string does not represent hex byte
|
/// initiates duid_template_ value with it.
|
||||||
|
/// \todo add support to generate DUIDs other than based on
|
||||||
|
/// 6-octets long MACs (e.g. DUID-UUID.
|
||||||
|
void generateDuidTemplate();
|
||||||
|
|
||||||
|
/// \brief Converts two-digit hexadecimal string to a byte.
|
||||||
|
///
|
||||||
|
/// \param hex_text Hexadecimal string e.g. AF.
|
||||||
|
/// \throw isc::InvalidParameter if string does not represent hex byte.
|
||||||
uint8_t convertHexString(const std::string& hex_text) const;
|
uint8_t convertHexString(const std::string& hex_text) const;
|
||||||
|
|
||||||
uint8_t ipversion_; ///< IP protocol version to be used, expected values are:
|
uint8_t ipversion_; ///< IP protocol version to be used, expected values are:
|
||||||
@@ -353,9 +371,9 @@ private:
|
|||||||
int report_delay_; ///< Delay between generation of two consecutive
|
int report_delay_; ///< Delay between generation of two consecutive
|
||||||
///< performance reports
|
///< performance reports
|
||||||
uint32_t clients_num_; ///< Number of simulated clients (aka randomization range).
|
uint32_t clients_num_; ///< Number of simulated clients (aka randomization range).
|
||||||
std::vector<uint8_t> mac_prefix_; ///< MAC address prefix used to generate unique DUIDs
|
std::vector<uint8_t> mac_template_; ///< MAC address template used to generate unique DUIDs
|
||||||
///< for simulated clients.
|
///< for simulated clients.
|
||||||
std::vector<uint8_t> duid_prefix_; ///< DUID prefix used to generate unique DUIDs for
|
std::vector<uint8_t> duid_template_; ///< DUID template used to generate unique DUIDs for
|
||||||
///< simulated clients
|
///< simulated clients
|
||||||
std::vector<std::string> base_; ///< Collection of base values specified with -b<value>
|
std::vector<std::string> base_; ///< Collection of base values specified with -b<value>
|
||||||
///< options. Supported "bases" are mac=<mac> and duid=<duid>
|
///< options. Supported "bases" are mac=<mac> and duid=<duid>
|
||||||
@@ -404,6 +422,7 @@ private:
|
|||||||
std::string wrapped_; ///< Wrapped command specified as -w<value>. Expected
|
std::string wrapped_; ///< Wrapped command specified as -w<value>. Expected
|
||||||
///< values are start and stop.
|
///< values are start and stop.
|
||||||
std::string server_name_; ///< Server name specified as last argument of command line.
|
std::string server_name_; ///< Server name specified as last argument of command line.
|
||||||
|
std::string commandline_; ///< Entire command line as typed in by the user.
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace perfdhcp
|
} // namespace perfdhcp
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
#define __LOCALIZED_OPTION_H
|
#define __LOCALIZED_OPTION_H
|
||||||
|
|
||||||
#include <dhcp/pkt6.h>
|
#include <dhcp/pkt6.h>
|
||||||
|
#include <dhcp/option6_ia.h>
|
||||||
|
#include <util/buffer.h>
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
namespace perfdhcp {
|
namespace perfdhcp {
|
||||||
@@ -42,56 +44,30 @@ namespace perfdhcp {
|
|||||||
///
|
///
|
||||||
class LocalizedOption : public dhcp::Option {
|
class LocalizedOption : public dhcp::Option {
|
||||||
public:
|
public:
|
||||||
/// \brief Constructor, sets default (0) option offset
|
|
||||||
///
|
|
||||||
/// \param u specifies universe (V4 or V6)
|
|
||||||
/// \param type option type (0-255 for V4 and 0-65535 for V6)
|
|
||||||
/// \param data content of the option
|
|
||||||
LocalizedOption(dhcp::Option::Universe u,
|
|
||||||
uint16_t type,
|
|
||||||
const dhcp::OptionBuffer& data) :
|
|
||||||
dhcp::Option(u, type, data),
|
|
||||||
offset_(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// \brief Constructor, used to create localized option from buffer.
|
||||||
/// \brief Constructor, used to create localized option from buffer
|
|
||||||
///
|
///
|
||||||
/// \param u specifies universe (V4 or V6)
|
/// This constructor creates localized option using whole provided
|
||||||
/// \param type option type (0-255 for V4 and 0-65535 for V6)
|
/// option buffer.
|
||||||
/// \param data content of the option
|
///
|
||||||
/// \param offset location of option in a packet (zero is default)
|
/// \param u universe (V4 or V6).
|
||||||
|
/// \param type option type (0-255 for V4 and 0-65535 for V6).
|
||||||
|
/// Option values 0 and 255 (v4) and 0 (v6) are not valid option
|
||||||
|
/// codes but they are accepted here for the server testing purposes.
|
||||||
|
/// \param data content of the option.
|
||||||
|
/// \param offset location of option in a packet (zero is default).
|
||||||
LocalizedOption(dhcp::Option::Universe u,
|
LocalizedOption(dhcp::Option::Universe u,
|
||||||
uint16_t type,
|
uint16_t type,
|
||||||
const dhcp::OptionBuffer& data,
|
const dhcp::OptionBuffer& data,
|
||||||
const size_t offset) :
|
const size_t offset = 0) :
|
||||||
dhcp::Option(u, type, data),
|
dhcp::Option(u, type, data),
|
||||||
offset_(offset) {
|
offset_(offset), option_valid_(true) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Constructor, sets default (0) option offset
|
/// \brief Constructor, used to create option from buffer iterators.
|
||||||
///
|
///
|
||||||
/// This contructor is similar to the previous one, but it does not take
|
/// This constructor creates localized option using part of the
|
||||||
/// the whole vector<uint8_t>, but rather subset of it.
|
/// option buffer pointed by iterators.
|
||||||
///
|
|
||||||
/// \param u specifies universe (V4 or V6)
|
|
||||||
/// \param type option type (0-255 for V4 and 0-65535 for V6)
|
|
||||||
/// \param first iterator to the first element that should be copied
|
|
||||||
/// \param last iterator to the next element after the last one
|
|
||||||
/// to be copied.
|
|
||||||
LocalizedOption(dhcp::Option::Universe u,
|
|
||||||
uint16_t type,
|
|
||||||
dhcp::OptionBufferConstIter first,
|
|
||||||
dhcp::OptionBufferConstIter last) :
|
|
||||||
dhcp::Option(u, type, first, last),
|
|
||||||
offset_(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// \brief Constructor, used to create option from buffer iterators
|
|
||||||
///
|
|
||||||
/// This contructor is similar to the previous one, but it does not take
|
|
||||||
/// the whole vector<uint8_t>, but rather subset of it.
|
|
||||||
///
|
///
|
||||||
/// \param u specifies universe (V4 or V6)
|
/// \param u specifies universe (V4 or V6)
|
||||||
/// \param type option type (0-255 for V4 and 0-65535 for V6)
|
/// \param type option type (0-255 for V4 and 0-65535 for V6)
|
||||||
@@ -102,9 +78,52 @@ public:
|
|||||||
LocalizedOption(dhcp::Option::Universe u,
|
LocalizedOption(dhcp::Option::Universe u,
|
||||||
uint16_t type,
|
uint16_t type,
|
||||||
dhcp::OptionBufferConstIter first,
|
dhcp::OptionBufferConstIter first,
|
||||||
dhcp::OptionBufferConstIter last, const size_t offset) :
|
dhcp::OptionBufferConstIter last,
|
||||||
|
const size_t offset = 0) :
|
||||||
dhcp::Option(u, type, first, last),
|
dhcp::Option(u, type, first, last),
|
||||||
offset_(offset) {
|
offset_(offset), option_valid_(true) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Copy constructor, creates LocalizedOption from Option6IA.
|
||||||
|
///
|
||||||
|
/// This copy constructor creates regular option from Option6IA.
|
||||||
|
/// The data from Option6IA data members are copied to
|
||||||
|
/// option buffer in appropriate sequence.
|
||||||
|
///
|
||||||
|
/// \param opt_ia option to be copied.
|
||||||
|
/// \param offset location of the option in a packet.
|
||||||
|
LocalizedOption(const boost::shared_ptr<dhcp::Option6IA>& opt_ia,
|
||||||
|
const size_t offset) :
|
||||||
|
dhcp::Option(Option::V6, 0, dhcp::OptionBuffer()),
|
||||||
|
offset_(offset), option_valid_(false) {
|
||||||
|
// If given option is NULL we will mark this new option
|
||||||
|
// as invalid. User may query if option is valid when
|
||||||
|
// object is created.
|
||||||
|
if (opt_ia) {
|
||||||
|
// Set universe and type.
|
||||||
|
universe_ = opt_ia->getUniverse();
|
||||||
|
type_ = opt_ia->getType();
|
||||||
|
util::OutputBuffer buf(opt_ia->len() - opt_ia->getHeaderLen());
|
||||||
|
try {
|
||||||
|
// Try to pack option data into the temporary buffer.
|
||||||
|
opt_ia->pack(buf);
|
||||||
|
if (buf.getLength() > 0) {
|
||||||
|
const char* buf_data = static_cast<const char*>(buf.getData());
|
||||||
|
// Option has been packed along with option type flag
|
||||||
|
// and transaction id so we have to skip first 4 bytes
|
||||||
|
// when copying temporary buffer option buffer.
|
||||||
|
data_.assign(buf_data + 4, buf_data + buf.getLength());
|
||||||
|
}
|
||||||
|
option_valid_ = true;
|
||||||
|
} catch (const Exception&) {
|
||||||
|
// If there was an exception somewhere when packing
|
||||||
|
// the data into the buffer we assume that option is
|
||||||
|
// not valid and should not be used.
|
||||||
|
option_valid_ = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
option_valid_ = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Returns offset of an option in a DHCP packet.
|
/// \brief Returns offset of an option in a DHCP packet.
|
||||||
@@ -112,12 +131,20 @@ public:
|
|||||||
/// \return option offset in a packet
|
/// \return option offset in a packet
|
||||||
size_t getOffset() const { return offset_; };
|
size_t getOffset() const { return offset_; };
|
||||||
|
|
||||||
|
/// \brief Checks if option is valid.
|
||||||
|
///
|
||||||
|
/// \return true, if option is valid.
|
||||||
|
virtual bool valid() {
|
||||||
|
return (Option::valid() && option_valid_);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t offset_; ///< Offset of DHCP option in a packet
|
size_t offset_; ///< Offset of DHCP option in a packet
|
||||||
|
bool option_valid_; ///< Is option valid.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace perfdhcp
|
} // namespace isc::perfdhcp
|
||||||
} // namespace isc
|
} // namespace isc
|
||||||
|
|
||||||
#endif // __LOCALIZED_OPTION_H
|
#endif // __LOCALIZED_OPTION_H
|
||||||
|
50
tests/tools/perfdhcp/main.cc
Normal file
50
tests/tools/perfdhcp/main.cc
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <exceptions/exceptions.h>
|
||||||
|
|
||||||
|
#include "test_control.h"
|
||||||
|
#include "command_options.h"
|
||||||
|
|
||||||
|
using namespace isc::perfdhcp;
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char* argv[]) {
|
||||||
|
CommandOptions& command_options = CommandOptions::instance();
|
||||||
|
try {
|
||||||
|
command_options.parse(argc, argv);
|
||||||
|
} catch(isc::Exception& e) {
|
||||||
|
std::cout << "Error parsing command line options: "
|
||||||
|
<< e.what() << std::endl;
|
||||||
|
command_options.usage();
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
try{
|
||||||
|
TestControl& test_control = TestControl::instance();
|
||||||
|
test_control.run();
|
||||||
|
} catch (isc::Exception& e) {
|
||||||
|
std::cout << "Error running perfdhcp: " << e.what() << std::endl;
|
||||||
|
std::string diags(command_options.getDiags());
|
||||||
|
if (diags.find('e') != std::string::npos) {
|
||||||
|
std::cout << "Fatal error" << std::endl;
|
||||||
|
}
|
||||||
|
return(1);
|
||||||
|
}
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
|
@@ -16,7 +16,6 @@
|
|||||||
#include <dhcp/dhcp6.h>
|
#include <dhcp/dhcp6.h>
|
||||||
|
|
||||||
#include "perf_pkt4.h"
|
#include "perf_pkt4.h"
|
||||||
#include "pkt_transform.h"
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace isc;
|
using namespace isc;
|
||||||
@@ -58,5 +57,14 @@ PerfPkt4::rawUnpack() {
|
|||||||
return (res);
|
return (res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PerfPkt4::writeAt(size_t dest_pos,
|
||||||
|
std::vector<uint8_t>::iterator first,
|
||||||
|
std::vector<uint8_t>::iterator last) {
|
||||||
|
return (PktTransform::writeAt(data_, dest_pos, first, last));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace perfdhcp
|
} // namespace perfdhcp
|
||||||
} // namespace isc
|
} // namespace isc
|
||||||
|
@@ -20,6 +20,7 @@
|
|||||||
#include <dhcp/pkt4.h>
|
#include <dhcp/pkt4.h>
|
||||||
|
|
||||||
#include "localized_option.h"
|
#include "localized_option.h"
|
||||||
|
#include "pkt_transform.h"
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
namespace perfdhcp {
|
namespace perfdhcp {
|
||||||
@@ -102,11 +103,36 @@ public:
|
|||||||
/// \return false If unpack operation failed.
|
/// \return false If unpack operation failed.
|
||||||
bool rawUnpack();
|
bool rawUnpack();
|
||||||
|
|
||||||
|
/// \brief Replace contents of buffer with data.
|
||||||
|
///
|
||||||
|
/// Function replaces part of the buffer with data from vector.
|
||||||
|
///
|
||||||
|
/// \param dest_pos position in buffer where data is replaced.
|
||||||
|
/// \param first beginning of data range in source vector.
|
||||||
|
/// \param last end of data range in source vector.
|
||||||
|
void writeAt(size_t dest_pos,
|
||||||
|
std::vector<uint8_t>::iterator first,
|
||||||
|
std::vector<uint8_t>::iterator last);
|
||||||
|
|
||||||
|
/// \brief Replace contents of buffer with value.
|
||||||
|
///
|
||||||
|
/// Function replaces part of buffer with value.
|
||||||
|
///
|
||||||
|
/// \param dest_pos position in buffer where value is
|
||||||
|
/// to be written.
|
||||||
|
/// \param val value to be written.
|
||||||
|
template<typename T>
|
||||||
|
void writeValueAt(size_t dest_pos, T val) {
|
||||||
|
PktTransform::writeValueAt<T>(data_, dest_pos, val);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t transid_offset_; ///< transaction id offset
|
size_t transid_offset_; ///< transaction id offset
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef boost::shared_ptr<PerfPkt4> PerfPkt4Ptr;
|
||||||
|
|
||||||
} // namespace perfdhcp
|
} // namespace perfdhcp
|
||||||
} // namespace isc
|
} // namespace isc
|
||||||
|
|
||||||
|
@@ -60,5 +60,13 @@ PerfPkt6::rawUnpack() {
|
|||||||
return (res);
|
return (res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PerfPkt6::writeAt(size_t dest_pos,
|
||||||
|
std::vector<uint8_t>::iterator first,
|
||||||
|
std::vector<uint8_t>::iterator last) {
|
||||||
|
return (PktTransform::writeAt(data_, dest_pos, first, last));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace perfdhcp
|
} // namespace perfdhcp
|
||||||
} // namespace isc
|
} // namespace isc
|
||||||
|
@@ -20,6 +20,7 @@
|
|||||||
#include <dhcp/pkt6.h>
|
#include <dhcp/pkt6.h>
|
||||||
|
|
||||||
#include "localized_option.h"
|
#include "localized_option.h"
|
||||||
|
#include "pkt_transform.h"
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
namespace perfdhcp {
|
namespace perfdhcp {
|
||||||
@@ -102,11 +103,36 @@ public:
|
|||||||
/// \return false if unpack operation failed.
|
/// \return false if unpack operation failed.
|
||||||
bool rawUnpack();
|
bool rawUnpack();
|
||||||
|
|
||||||
|
/// \brief Replace contents of buffer with data.
|
||||||
|
///
|
||||||
|
/// Function replaces part of the buffer with data from vector.
|
||||||
|
///
|
||||||
|
/// \param dest_pos position in buffer where data is replaced.
|
||||||
|
/// \param first beginning of data range in source vector.
|
||||||
|
/// \param last end of data range in source vector.
|
||||||
|
void writeAt(size_t dest_pos,
|
||||||
|
std::vector<uint8_t>::iterator first,
|
||||||
|
std::vector<uint8_t>::iterator last);
|
||||||
|
|
||||||
|
/// \brief Replace contents of buffer with value.
|
||||||
|
///
|
||||||
|
/// Function replaces part of buffer with value.
|
||||||
|
///
|
||||||
|
/// \param dest_pos position in buffer where value is
|
||||||
|
/// to be written.
|
||||||
|
/// \param val value to be written.
|
||||||
|
template<typename T>
|
||||||
|
void writeValueAt(size_t dest_pos, T val) {
|
||||||
|
PktTransform::writeValueAt<T>(data_, dest_pos, val);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t transid_offset_; ///< transaction id offset
|
size_t transid_offset_; ///< transaction id offset
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef boost::shared_ptr<PerfPkt6> PerfPkt6Ptr;
|
||||||
|
|
||||||
} // namespace perfdhcp
|
} // namespace perfdhcp
|
||||||
} // namespace isc
|
} // namespace isc
|
||||||
|
|
||||||
|
@@ -216,7 +216,13 @@ PktTransform::unpackOptions(const OptionBuffer& in_buffer,
|
|||||||
in_buffer.begin() + offset + opt_len);
|
in_buffer.begin() + offset + opt_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PktTransform::writeAt(dhcp::OptionBuffer& in_buffer, size_t dest_pos,
|
||||||
|
dhcp::OptionBuffer::iterator first,
|
||||||
|
dhcp::OptionBuffer::iterator last) {
|
||||||
|
memcpy(&in_buffer[dest_pos], &(*first), std::distance(first, last));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace perfdhcp
|
} // namespace perfdhcp
|
||||||
} // namespace isc
|
} // namespace isc
|
||||||
|
@@ -92,6 +92,35 @@ public:
|
|||||||
const size_t transid_offset,
|
const size_t transid_offset,
|
||||||
uint32_t& transid);
|
uint32_t& transid);
|
||||||
|
|
||||||
|
/// \brief Replace contents of buffer with vector.
|
||||||
|
///
|
||||||
|
/// Function replaces data of the buffer with data from vector.
|
||||||
|
///
|
||||||
|
/// \param in_buffer destination buffer.
|
||||||
|
/// \param dest_pos position in destination buffer.
|
||||||
|
/// \param first beginning of data range in source vector.
|
||||||
|
/// \param last end of data range in source vector.
|
||||||
|
static void writeAt(dhcp::OptionBuffer& in_buffer, size_t dest_pos,
|
||||||
|
std::vector<uint8_t>::iterator first,
|
||||||
|
std::vector<uint8_t>::iterator last);
|
||||||
|
|
||||||
|
/// \brief Replace contents of one vector with uint16 value.
|
||||||
|
///
|
||||||
|
/// Function replaces data inside one vector with uint16_t value.
|
||||||
|
///
|
||||||
|
/// \param in_buffer destination buffer.
|
||||||
|
/// \param dest_pos position in destination buffer.
|
||||||
|
/// \param val value to be written.
|
||||||
|
template<typename T>
|
||||||
|
static void writeValueAt(dhcp::OptionBuffer& in_buffer, size_t dest_pos,
|
||||||
|
T val) {
|
||||||
|
// @todo consider replacing the loop with switch statement
|
||||||
|
// checking sizeof(T).
|
||||||
|
for (int i = 0; i < sizeof(T); ++i) {
|
||||||
|
in_buffer[dest_pos + i] = (val >> 8 * (sizeof(T) - i - 1)) & 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// \brief Replaces contents of options in a buffer.
|
/// \brief Replaces contents of options in a buffer.
|
||||||
///
|
///
|
||||||
@@ -131,6 +160,7 @@ private:
|
|||||||
/// \throw isc::Unexpected if options unpack failed.
|
/// \throw isc::Unexpected if options unpack failed.
|
||||||
static void unpackOptions(const dhcp::OptionBuffer& in_buffer,
|
static void unpackOptions(const dhcp::OptionBuffer& in_buffer,
|
||||||
const dhcp::Option::OptionCollection& options);
|
const dhcp::Option::OptionCollection& options);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace perfdhcp
|
} // namespace perfdhcp
|
||||||
|
@@ -47,8 +47,8 @@ namespace perfdhcp {
|
|||||||
/// stored on the list of sent packets. When packets are matched the
|
/// stored on the list of sent packets. When packets are matched the
|
||||||
/// round trip time can be calculated.
|
/// round trip time can be calculated.
|
||||||
///
|
///
|
||||||
/// \tparam T class representing DHCPv4 or DHCPv6 packet.
|
/// \param T class representing DHCPv4 or DHCPv6 packet.
|
||||||
template <class T>
|
template <class T = dhcp::Pkt4>
|
||||||
class StatsMgr : public boost::noncopyable {
|
class StatsMgr : public boost::noncopyable {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ public:
|
|||||||
/// \param packet packet which transaction id is to be hashed.
|
/// \param packet packet which transaction id is to be hashed.
|
||||||
/// \throw isc::BadValue if packet is null.
|
/// \throw isc::BadValue if packet is null.
|
||||||
/// \return transaction id hash.
|
/// \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) {
|
if (!packet) {
|
||||||
isc_throw(BadValue, "Packet is null");
|
isc_throw(BadValue, "Packet is null");
|
||||||
}
|
}
|
||||||
@@ -214,21 +214,33 @@ public:
|
|||||||
/// }
|
/// }
|
||||||
/// \endcode
|
/// \endcode
|
||||||
typedef boost::multi_index_container<
|
typedef boost::multi_index_container<
|
||||||
boost::shared_ptr<const T>,
|
// Container holds shared_ptr<Pkt4> or shared_ptr<Pkt6> objects.
|
||||||
|
boost::shared_ptr<T>,
|
||||||
|
// List container indexes.
|
||||||
boost::multi_index::indexed_by<
|
boost::multi_index::indexed_by<
|
||||||
|
// Sequenced index provides the way to use this container
|
||||||
|
// in the same way as std::list.
|
||||||
boost::multi_index::sequenced<>,
|
boost::multi_index::sequenced<>,
|
||||||
|
// The other index keeps products of transaction id.
|
||||||
boost::multi_index::hashed_non_unique<
|
boost::multi_index::hashed_non_unique<
|
||||||
boost::multi_index::global_fun<
|
// Specify hash function to get the product of
|
||||||
const boost::shared_ptr<const T>&,
|
// transaction id. This product is obtained by calling
|
||||||
uint32_t,
|
// hashTransid() function.
|
||||||
&ExchangeStats::hashTransid
|
boost::multi_index::global_fun<
|
||||||
>
|
// Hashing function takes shared_ptr<Pkt4> or
|
||||||
|
// shared_ptr<Pkt6> as argument.
|
||||||
|
const boost::shared_ptr<T>&,
|
||||||
|
// ... and returns uint32 value.
|
||||||
|
uint32_t,
|
||||||
|
// ... and here is a reference to it.
|
||||||
|
&ExchangeStats::hashTransid
|
||||||
|
>
|
||||||
>
|
>
|
||||||
>
|
>
|
||||||
> PktList;
|
> PktList;
|
||||||
|
|
||||||
/// Packet list iterator for sequencial access to elements.
|
/// 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.
|
/// Packet list index to search packets using transaction id hash.
|
||||||
typedef typename PktList::template nth_index<1>::type
|
typedef typename PktList::template nth_index<1>::type
|
||||||
PktListTransidHashIndex;
|
PktListTransidHashIndex;
|
||||||
@@ -243,20 +255,21 @@ public:
|
|||||||
/// In this mode all packets are stored throughout the test execution.
|
/// In this mode all packets are stored throughout the test execution.
|
||||||
ExchangeStats(const ExchangeType xchg_type, const bool archive_enabled)
|
ExchangeStats(const ExchangeType xchg_type, const bool archive_enabled)
|
||||||
: xchg_type_(xchg_type),
|
: xchg_type_(xchg_type),
|
||||||
min_delay_(std::numeric_limits<double>::max()),
|
|
||||||
max_delay_(0.),
|
|
||||||
sum_delay_(0.),
|
|
||||||
orphans_(0),
|
|
||||||
sum_delay_squared_(0.),
|
|
||||||
ordered_lookups_(0),
|
|
||||||
unordered_lookup_size_sum_(0),
|
|
||||||
unordered_lookups_(0),
|
|
||||||
sent_packets_num_(0),
|
|
||||||
rcvd_packets_num_(0),
|
|
||||||
sent_packets_(),
|
sent_packets_(),
|
||||||
rcvd_packets_(),
|
rcvd_packets_(),
|
||||||
archived_packets_(),
|
archived_packets_(),
|
||||||
archive_enabled_(archive_enabled) {
|
archive_enabled_(archive_enabled),
|
||||||
|
min_delay_(std::numeric_limits<double>::max()),
|
||||||
|
max_delay_(0.),
|
||||||
|
sum_delay_(0.),
|
||||||
|
sum_delay_squared_(0.),
|
||||||
|
orphans_(0),
|
||||||
|
unordered_lookup_size_sum_(0),
|
||||||
|
unordered_lookups_(0),
|
||||||
|
ordered_lookups_(0),
|
||||||
|
sent_packets_num_(0),
|
||||||
|
rcvd_packets_num_(0)
|
||||||
|
{
|
||||||
next_sent_ = sent_packets_.begin();
|
next_sent_ = sent_packets_.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,7 +279,7 @@ public:
|
|||||||
///
|
///
|
||||||
/// \param packet packet object to be added.
|
/// \param packet packet object to be added.
|
||||||
/// \throw isc::BadValue if packet is null.
|
/// \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) {
|
if (!packet) {
|
||||||
isc_throw(BadValue, "Packet is null");
|
isc_throw(BadValue, "Packet is null");
|
||||||
}
|
}
|
||||||
@@ -280,7 +293,7 @@ public:
|
|||||||
///
|
///
|
||||||
/// \param packet packet object to be added.
|
/// \param packet packet object to be added.
|
||||||
/// \throw isc::BadValue if packet is null.
|
/// \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) {
|
if (!packet) {
|
||||||
isc_throw(BadValue, "Packet is null");
|
isc_throw(BadValue, "Packet is null");
|
||||||
}
|
}
|
||||||
@@ -296,8 +309,8 @@ public:
|
|||||||
/// \param rcvd_packet received packet
|
/// \param rcvd_packet received packet
|
||||||
/// \throw isc::BadValue if sent or received packet is null.
|
/// \throw isc::BadValue if sent or received packet is null.
|
||||||
/// \throw isc::Unexpected if failed to calculate timestamps
|
/// \throw isc::Unexpected if failed to calculate timestamps
|
||||||
void updateDelays(const boost::shared_ptr<const T>& sent_packet,
|
void updateDelays(const boost::shared_ptr<T>& sent_packet,
|
||||||
const boost::shared_ptr<const T>& rcvd_packet) {
|
const boost::shared_ptr<T>& rcvd_packet) {
|
||||||
if (!sent_packet) {
|
if (!sent_packet) {
|
||||||
isc_throw(BadValue, "Sent packet is null");
|
isc_throw(BadValue, "Sent packet is null");
|
||||||
}
|
}
|
||||||
@@ -355,7 +368,8 @@ public:
|
|||||||
/// \throw isc::BadValue if received packet is null.
|
/// \throw isc::BadValue if received packet is null.
|
||||||
/// \return packet having specified transaction or NULL if packet
|
/// \return packet having specified transaction or NULL if packet
|
||||||
/// not found
|
/// 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) {
|
if (!rcvd_packet) {
|
||||||
isc_throw(BadValue, "Received packet is null");
|
isc_throw(BadValue, "Received packet is null");
|
||||||
}
|
}
|
||||||
@@ -366,7 +380,7 @@ public:
|
|||||||
// that the received packet we got has no corresponding
|
// that the received packet we got has no corresponding
|
||||||
// sent packet so orphans counter has to be updated.
|
// sent packet so orphans counter has to be updated.
|
||||||
++orphans_;
|
++orphans_;
|
||||||
return(boost::shared_ptr<const T>());
|
return(boost::shared_ptr<T>());
|
||||||
} else if (next_sent_ == sent_packets_.end()) {
|
} else if (next_sent_ == sent_packets_.end()) {
|
||||||
// Even if there are still many unmatched packets on the
|
// Even if there are still many unmatched packets on the
|
||||||
// list we might hit the end of it because of unordered
|
// list we might hit the end of it because of unordered
|
||||||
@@ -425,13 +439,13 @@ public:
|
|||||||
// If we are here, it means that both ordered lookup and
|
// If we are here, it means that both ordered lookup and
|
||||||
// unordered lookup failed. Searched packet is not on the list.
|
// unordered lookup failed. Searched packet is not on the list.
|
||||||
++orphans_;
|
++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
|
// Packet is matched so we count it. We don't count unmatched packets
|
||||||
// as they are counted as orphans with a separate counter.
|
// as they are counted as orphans with a separate counter.
|
||||||
++rcvd_packets_num_;
|
++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
|
// If packet was found, we assume it will be never searched
|
||||||
// again. We want to delete this packet from the list to
|
// again. We want to delete this packet from the list to
|
||||||
// improve performance of future searches.
|
// improve performance of future searches.
|
||||||
@@ -548,6 +562,19 @@ public:
|
|||||||
/// \return number of received packets.
|
/// \return number of received packets.
|
||||||
uint64_t getRcvdPacketsNum() const { return(rcvd_packets_num_); }
|
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.
|
/// \brief Print main statistics for packet exchange.
|
||||||
///
|
///
|
||||||
/// Method prints main statistics for particular exchange.
|
/// Method prints main statistics for particular exchange.
|
||||||
@@ -555,10 +582,9 @@ public:
|
|||||||
/// number of dropped packets and number of orphans.
|
/// number of dropped packets and number of orphans.
|
||||||
void printMainStats() const {
|
void printMainStats() const {
|
||||||
using namespace std;
|
using namespace std;
|
||||||
uint64_t drops = getRcvdPacketsNum() - getSentPacketsNum();
|
|
||||||
cout << "sent packets: " << getSentPacketsNum() << endl
|
cout << "sent packets: " << getSentPacketsNum() << endl
|
||||||
<< "received packets: " << getRcvdPacketsNum() << endl
|
<< "received packets: " << getRcvdPacketsNum() << endl
|
||||||
<< "drops: " << drops << endl
|
<< "drops: " << getDroppedPacketsNum() << endl
|
||||||
<< "orphans: " << getOrphans() << endl;
|
<< "orphans: " << getOrphans() << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -610,7 +636,7 @@ public:
|
|||||||
for (PktListIterator it = rcvd_packets_.begin();
|
for (PktListIterator it = rcvd_packets_.begin();
|
||||||
it != rcvd_packets_.end();
|
it != rcvd_packets_.end();
|
||||||
++it) {
|
++it) {
|
||||||
boost::shared_ptr<const T> rcvd_packet = *it;
|
boost::shared_ptr<T> rcvd_packet = *it;
|
||||||
PktListTransidHashIndex& idx =
|
PktListTransidHashIndex& idx =
|
||||||
archived_packets_.template get<1>();
|
archived_packets_.template get<1>();
|
||||||
std::pair<PktListTransidHashIterator,
|
std::pair<PktListTransidHashIterator,
|
||||||
@@ -621,7 +647,7 @@ public:
|
|||||||
++it) {
|
++it) {
|
||||||
if ((*it_archived)->getTransid() ==
|
if ((*it_archived)->getTransid() ==
|
||||||
rcvd_packet->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.
|
// Get sent and received packet times.
|
||||||
ptime sent_time = sent_packet->getTimestamp();
|
ptime sent_time = sent_packet->getTimestamp();
|
||||||
ptime rcvd_time = rcvd_packet->getTimestamp();
|
ptime rcvd_time = rcvd_packet->getTimestamp();
|
||||||
@@ -761,7 +787,8 @@ public:
|
|||||||
StatsMgr(const bool archive_enabled = false) :
|
StatsMgr(const bool archive_enabled = false) :
|
||||||
exchanges_(),
|
exchanges_(),
|
||||||
custom_counters_(),
|
custom_counters_(),
|
||||||
archive_enabled_(archive_enabled) {
|
archive_enabled_(archive_enabled),
|
||||||
|
boot_time_(boost::posix_time::microsec_clock::universal_time()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Specify new exchange type.
|
/// \brief Specify new exchange type.
|
||||||
@@ -819,7 +846,7 @@ public:
|
|||||||
///
|
///
|
||||||
/// \param counter_key key poitinh to the counter in the counters map.
|
/// \param counter_key key poitinh to the counter in the counters map.
|
||||||
/// \return pointer to specified counter after incrementation.
|
/// \return pointer to specified counter after incrementation.
|
||||||
const CustomCounter& IncrementCounter(const std::string& counter_key) {
|
const CustomCounter& incrementCounter(const std::string& counter_key) {
|
||||||
CustomCounterPtr counter = getCounter(counter_key);
|
CustomCounterPtr counter = getCounter(counter_key);
|
||||||
return(++(*counter));
|
return(++(*counter));
|
||||||
}
|
}
|
||||||
@@ -835,7 +862,7 @@ public:
|
|||||||
/// \throw isc::BadValue if invalid exchange type specified or
|
/// \throw isc::BadValue if invalid exchange type specified or
|
||||||
/// packet is null.
|
/// packet is null.
|
||||||
void passSentPacket(const ExchangeType xchg_type,
|
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);
|
ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
|
||||||
xchg_stats->appendSent(packet);
|
xchg_stats->appendSent(packet);
|
||||||
}
|
}
|
||||||
@@ -853,10 +880,11 @@ public:
|
|||||||
/// or packet is null.
|
/// or packet is null.
|
||||||
/// \throw isc::Unexpected if corresponding packet was not
|
/// \throw isc::Unexpected if corresponding packet was not
|
||||||
/// found on the list of sent packets.
|
/// found on the list of sent packets.
|
||||||
void passRcvdPacket(const ExchangeType xchg_type,
|
boost::shared_ptr<T>
|
||||||
const boost::shared_ptr<const T>& packet) {
|
passRcvdPacket(const ExchangeType xchg_type,
|
||||||
|
const boost::shared_ptr<T>& packet) {
|
||||||
ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
|
ExchangeStatsPtr xchg_stats = getExchangeStats(xchg_type);
|
||||||
boost::shared_ptr<const T> sent_packet
|
boost::shared_ptr<T> sent_packet
|
||||||
= xchg_stats->matchPackets(packet);
|
= xchg_stats->matchPackets(packet);
|
||||||
|
|
||||||
if (sent_packet) {
|
if (sent_packet) {
|
||||||
@@ -865,6 +893,7 @@ public:
|
|||||||
xchg_stats->appendRcvd(packet);
|
xchg_stats->appendRcvd(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return(sent_packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Return minumum delay between sent and received packet.
|
/// \brief Return minumum delay between sent and received packet.
|
||||||
@@ -999,6 +1028,33 @@ public:
|
|||||||
return(xchg_stats->getRcvdPacketsNum());
|
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 Get time period since the start of test.
|
||||||
|
///
|
||||||
|
/// Calculate dna return period since the test start. This
|
||||||
|
/// can be specifically helpful when calculating packet
|
||||||
|
/// exchange rates.
|
||||||
|
///
|
||||||
|
/// \return test period so far.
|
||||||
|
boost::posix_time::time_period getTestPeriod() const {
|
||||||
|
using namespace boost::posix_time;
|
||||||
|
time_period test_period(boot_time_,
|
||||||
|
microsec_clock::universal_time());
|
||||||
|
return test_period;
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Return name of the exchange.
|
/// \brief Return name of the exchange.
|
||||||
///
|
///
|
||||||
/// Method returns name of the specified exchange type.
|
/// Method returns name of the specified exchange type.
|
||||||
@@ -1052,6 +1108,32 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \brief Print intermediate statistics.
|
||||||
|
///
|
||||||
|
/// Method prints intermediate statistics for all exchanges.
|
||||||
|
/// Statistics includes sent, received and dropped packets
|
||||||
|
/// counters.
|
||||||
|
void printIntermediateStats() const {
|
||||||
|
std::ostringstream stream_sent;
|
||||||
|
std::ostringstream stream_rcvd;
|
||||||
|
std::ostringstream stream_drops;
|
||||||
|
std::string sep("");
|
||||||
|
for (ExchangesMapIterator it = exchanges_.begin();
|
||||||
|
it != exchanges_.end(); ++it) {
|
||||||
|
|
||||||
|
if (it != exchanges_.begin()) {
|
||||||
|
sep = "/";
|
||||||
|
}
|
||||||
|
stream_sent << sep << it->second->getSentPacketsNum();
|
||||||
|
stream_rcvd << sep << it->second->getRcvdPacketsNum();
|
||||||
|
stream_drops << sep << it->second->getDroppedPacketsNum();
|
||||||
|
}
|
||||||
|
std::cout << "sent: " << stream_sent.str()
|
||||||
|
<< "; received: " << stream_rcvd.str()
|
||||||
|
<< "; drops: " << stream_drops.str()
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
/// \brief Print timestamps of all packets.
|
/// \brief Print timestamps of all packets.
|
||||||
///
|
///
|
||||||
/// Method prints timestamps of all sent and received
|
/// Method prints timestamps of all sent and received
|
||||||
@@ -1129,6 +1211,8 @@ private:
|
|||||||
/// for extended period of time and many packets have to be
|
/// for extended period of time and many packets have to be
|
||||||
/// archived.
|
/// archived.
|
||||||
bool archive_enabled_;
|
bool archive_enabled_;
|
||||||
|
|
||||||
|
boost::posix_time::ptime boot_time_; ///< Time when test is started.
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace perfdhcp
|
} // namespace perfdhcp
|
||||||
|
8
tests/tools/perfdhcp/templates/Makefile.am
Normal file
8
tests/tools/perfdhcp/templates/Makefile.am
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
SUBDIRS = .
|
||||||
|
|
||||||
|
perfdhcpdir = $(pkgdatadir)
|
||||||
|
perfdhcp_DATA = discover-example.hex request4-example.hex \
|
||||||
|
solicit-example.hex request6-example.hex
|
||||||
|
|
||||||
|
EXTRA_DIST = discover-example.hex request4-example.hex
|
||||||
|
EXTRA_DIST += solicit-example.hex request6-example.hex
|
1
tests/tools/perfdhcp/templates/discover-example.hex
Normal file
1
tests/tools/perfdhcp/templates/discover-example.hex
Normal file
@@ -0,0 +1 @@
|
|||||||
|
01010601008b45d200000000000000000000000000000000ac100102000c0102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501013707011c02030f060cff
|
1
tests/tools/perfdhcp/templates/request4-example.hex
Normal file
1
tests/tools/perfdhcp/templates/request4-example.hex
Normal file
@@ -0,0 +1 @@
|
|||||||
|
01010601007b23f800000000000000000000000000000000ac100102000c0102030400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633204ac1001813501033604ac1001013707011c02030f060cff
|
1
tests/tools/perfdhcp/templates/request6-example.hex
Normal file
1
tests/tools/perfdhcp/templates/request6-example.hex
Normal file
@@ -0,0 +1 @@
|
|||||||
|
03da30c60001000e0001000117cf8e76000c010203060002000e0001000117cf8a5c080027a87b3400030028000000010000000a0000000e0005001820010db800010000000000000001b568000000be000000c8000800020000
|
1
tests/tools/perfdhcp/templates/solicit-example.hex
Normal file
1
tests/tools/perfdhcp/templates/solicit-example.hex
Normal file
@@ -0,0 +1 @@
|
|||||||
|
015f4e650001000e0001000117cf8e76000c010203040003000c0000000100000e01000015180006000400170018000800020000
|
1682
tests/tools/perfdhcp/test_control.cc
Normal file
1682
tests/tools/perfdhcp/test_control.cc
Normal file
File diff suppressed because it is too large
Load Diff
803
tests/tools/perfdhcp/test_control.h
Normal file
803
tests/tools/perfdhcp/test_control.h
Normal file
@@ -0,0 +1,803 @@
|
|||||||
|
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef __TEST_CONTROL_H
|
||||||
|
#define __TEST_CONTROL_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||||
|
|
||||||
|
#include <dhcp/iface_mgr.h>
|
||||||
|
#include <dhcp/dhcp6.h>
|
||||||
|
#include <dhcp/pkt4.h>
|
||||||
|
#include <dhcp/pkt6.h>
|
||||||
|
|
||||||
|
#include "stats_mgr.h"
|
||||||
|
|
||||||
|
namespace isc {
|
||||||
|
namespace perfdhcp {
|
||||||
|
|
||||||
|
/// \brief Test Control class.
|
||||||
|
///
|
||||||
|
/// This class is responsible for executing DHCP performance
|
||||||
|
/// test end to end.
|
||||||
|
///
|
||||||
|
/// Option factory functions are registered using
|
||||||
|
/// \ref dhcp::LibDHCP::OptionFactoryRegister. Registered factory functions
|
||||||
|
/// provide a way to create options of the same type in the same way.
|
||||||
|
/// When new option instance is needed the corresponding factory
|
||||||
|
/// function is called to create it. This is done by calling
|
||||||
|
/// \ref dhcp::Option::factory with DHCP message type specified as one of
|
||||||
|
/// parameters. Some of the parameters passed to factory function
|
||||||
|
/// may be ignored (e.g. option buffer).
|
||||||
|
/// Please note that naming convention for factory functions within this
|
||||||
|
/// class is as follows:
|
||||||
|
/// - factoryABC4 - factory function for DHCPv4 option,
|
||||||
|
/// - factoryDEF6 - factory function for DHCPv6 option,
|
||||||
|
/// - factoryGHI - factory function that can be used to create either
|
||||||
|
/// DHCPv4 or DHCPv6 option.
|
||||||
|
class TestControl : public boost::noncopyable {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Default transaction id offset.
|
||||||
|
static const size_t DHCPV4_TRANSID_OFFSET = 4;
|
||||||
|
/// Default offset of MAC's last octet.
|
||||||
|
static const size_t DHCPV4_RANDOMIZATION_OFFSET = 35;
|
||||||
|
/// Default elapsed time offset.
|
||||||
|
static const size_t DHCPV4_ELAPSED_TIME_OFFSET = 8;
|
||||||
|
/// Default server id offset.
|
||||||
|
static const size_t DHCPV4_SERVERID_OFFSET = 54;
|
||||||
|
/// Default requested ip offset.
|
||||||
|
static const size_t DHCPV4_REQUESTED_IP_OFFSET = 240;
|
||||||
|
/// Default DHCPV6 transaction id offset.
|
||||||
|
static const size_t DHCPV6_TRANSID_OFFSET = 1;
|
||||||
|
/// Default DHCPV6 randomization offset (last octet of DUID)
|
||||||
|
static const size_t DHCPV6_RANDOMIZATION_OFFSET = 21;
|
||||||
|
/// Default DHCPV6 elapsed time offset.
|
||||||
|
static const size_t DHCPV6_ELAPSED_TIME_OFFSET = 84;
|
||||||
|
/// Default DHCPV6 server id offset.
|
||||||
|
static const size_t DHCPV6_SERVERID_OFFSET = 22;
|
||||||
|
/// Default DHCPV6 IA_NA offset.
|
||||||
|
static const size_t DHCPV6_IA_NA_OFFSET = 40;
|
||||||
|
|
||||||
|
/// Statistics Manager for DHCPv4.
|
||||||
|
typedef StatsMgr<dhcp::Pkt4> StatsMgr4;
|
||||||
|
/// Pointer to Statistics Manager for DHCPv4;
|
||||||
|
typedef boost::shared_ptr<StatsMgr4> StatsMgr4Ptr;
|
||||||
|
/// Statictics Manager for DHCPv6.
|
||||||
|
typedef StatsMgr<dhcp::Pkt6> StatsMgr6;
|
||||||
|
/// Pointer to Statistics Manager for DHCPv6.
|
||||||
|
typedef boost::shared_ptr<StatsMgr6> StatsMgr6Ptr;
|
||||||
|
/// Packet exchange type.
|
||||||
|
typedef StatsMgr<>::ExchangeType ExchangeType;
|
||||||
|
/// Packet template buffer.
|
||||||
|
typedef std::vector<uint8_t> TemplateBuffer;
|
||||||
|
/// Packet template buffers list.
|
||||||
|
typedef std::vector<TemplateBuffer> TemplateBufferCollection;
|
||||||
|
|
||||||
|
/// \brief Socket wrapper structure.
|
||||||
|
///
|
||||||
|
/// This is the wrapper that holds descriptor of the socket
|
||||||
|
/// used to run DHCP test. The wrapped socket is closed in
|
||||||
|
/// the destructor. This prevents resource leaks when when
|
||||||
|
/// function that created the socket ends (normally or
|
||||||
|
/// when exception occurs). This structure extends parent
|
||||||
|
/// structure with new field ifindex_ that holds interface
|
||||||
|
/// index where socket is bound to.
|
||||||
|
struct TestControlSocket : public dhcp::IfaceMgr::SocketInfo {
|
||||||
|
/// Interface index.
|
||||||
|
uint16_t ifindex_;
|
||||||
|
/// Is socket valid. It will not be valid if the provided socket
|
||||||
|
/// descriptor does not point to valid socket.
|
||||||
|
bool valid_;
|
||||||
|
|
||||||
|
/// \brief Constructor of socket wrapper class.
|
||||||
|
///
|
||||||
|
/// This constructor uses provided socket descriptor to
|
||||||
|
/// find the name of the interface where socket has been
|
||||||
|
/// bound to. If provided socket descriptor is invalid then
|
||||||
|
/// valid_ field is set to false;
|
||||||
|
///
|
||||||
|
/// \param socket socket descriptor.
|
||||||
|
TestControlSocket(const int socket);
|
||||||
|
|
||||||
|
/// \brief Destriuctor of the socket wrapper class.
|
||||||
|
///
|
||||||
|
/// Destructor closes wrapped socket.
|
||||||
|
~TestControlSocket();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// \brief Initialize socket data.
|
||||||
|
///
|
||||||
|
/// This method initializes members of the class that Interface
|
||||||
|
/// Manager holds: interface name, local address.
|
||||||
|
///
|
||||||
|
/// \throw isc::BadValue if interface for specified socket
|
||||||
|
/// descriptor does not exist.
|
||||||
|
void initSocketData();
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Number generator class.
|
||||||
|
///
|
||||||
|
/// This is default numbers generator class. The member function is
|
||||||
|
/// used to generate uint32_t values. Other generator classes should
|
||||||
|
/// derive from this one to implement generation algorithms
|
||||||
|
/// (e.g. sequencial or based on random function).
|
||||||
|
class NumberGenerator {
|
||||||
|
public:
|
||||||
|
/// \brief Generate number.
|
||||||
|
///
|
||||||
|
/// \return Generate number.
|
||||||
|
virtual uint32_t generate() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The default generator pointer.
|
||||||
|
typedef boost::shared_ptr<NumberGenerator> NumberGeneratorPtr;
|
||||||
|
|
||||||
|
/// \brief Sequencial numbers generatorc class.
|
||||||
|
class SequencialGenerator : public NumberGenerator {
|
||||||
|
public:
|
||||||
|
/// \brief Constructor.
|
||||||
|
///
|
||||||
|
/// \param range maximum number generated. If 0 is given then
|
||||||
|
/// range defaults to maximym uint32_t value.
|
||||||
|
SequencialGenerator(uint32_t range = 0xFFFFFFFF) :
|
||||||
|
NumberGenerator(),
|
||||||
|
num_(0),
|
||||||
|
range_(range) {
|
||||||
|
if (range_ == 0) {
|
||||||
|
range_ = 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Generate number sequencialy.
|
||||||
|
///
|
||||||
|
/// \return generated number.
|
||||||
|
virtual uint32_t generate() {
|
||||||
|
uint32_t num = num_;
|
||||||
|
num_ = (num_ + 1) % range_;
|
||||||
|
return (num);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
uint32_t num_; ///< Current number.
|
||||||
|
uint32_t range_; ///< Maximum number generated.
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Length of the Ethernet HW address (MAC) in bytes.
|
||||||
|
///
|
||||||
|
/// \todo Make this variable length as there are cases when HW
|
||||||
|
/// address is longer than this (e.g. 20 bytes).
|
||||||
|
static const uint8_t HW_ETHER_LEN = 6;
|
||||||
|
|
||||||
|
/// TestControl is a singleton class. This method returns reference
|
||||||
|
/// to its sole instance.
|
||||||
|
///
|
||||||
|
/// \return the only existing instance of test control
|
||||||
|
static TestControl& instance();
|
||||||
|
|
||||||
|
/// brief\ Run performance test.
|
||||||
|
///
|
||||||
|
/// Method runs whole performance test. Command line options must
|
||||||
|
/// be parsed prior to running this function. Othewise function will
|
||||||
|
/// throw exception.
|
||||||
|
///
|
||||||
|
/// \throw isc::InvalidOperation if command line options are not parsed.
|
||||||
|
/// \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(const NumberGeneratorPtr& generator) {
|
||||||
|
transid_gen_.reset();
|
||||||
|
transid_gen_ = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Set new MAC address generator.
|
||||||
|
///
|
||||||
|
/// Set numbers generator that will be used to generate various
|
||||||
|
/// MAC addresses to simulate number of clients.
|
||||||
|
///
|
||||||
|
/// \param generator object to be used.
|
||||||
|
void setMacAddrGenerator(const NumberGeneratorPtr& generator) {
|
||||||
|
macaddr_gen_.reset();
|
||||||
|
macaddr_gen_ = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We would really like following methods and members to be private but
|
||||||
|
// they have to be accessible for unit-testing. Another, possibly better,
|
||||||
|
// solution is to make this class friend of test class but this is not
|
||||||
|
// what's followed in other classes.
|
||||||
|
protected:
|
||||||
|
/// \brief Default constructor.
|
||||||
|
///
|
||||||
|
/// Default constructor is protected as the object can be created
|
||||||
|
/// only via \ref instance method.
|
||||||
|
TestControl();
|
||||||
|
|
||||||
|
/// \brief Check if test exit condtitions fulfilled.
|
||||||
|
///
|
||||||
|
/// Method checks if the test exit conditions are fulfiled.
|
||||||
|
/// Exit conditions are checked periodically from the
|
||||||
|
/// main loop. Program should break the main loop when
|
||||||
|
/// this method returns true. It is calling function
|
||||||
|
/// responsibility to break main loop gracefully and
|
||||||
|
/// cleanup after test execution.
|
||||||
|
///
|
||||||
|
/// \return true if any of the exit conditions is fulfiled.
|
||||||
|
bool checkExitConditions() const;
|
||||||
|
|
||||||
|
/// \brief Factory function to create DHCPv6 ELAPSED_TIME option.
|
||||||
|
///
|
||||||
|
/// This factory function creates DHCPv6 ELAPSED_TIME option instance.
|
||||||
|
/// If empty buffer is passed the option buffer will be initialized
|
||||||
|
/// to length 2 and values will be initialized to zeros. Otherwise
|
||||||
|
/// function will initialize option buffer with values in passed buffer.
|
||||||
|
///
|
||||||
|
/// \param u universe (ignored)
|
||||||
|
/// \param type option-type (ignored).
|
||||||
|
/// \param buf option-buffer containing option content (2 bytes) or
|
||||||
|
/// empty buffer if option content has to be set to default (0) value.
|
||||||
|
/// \throw if elapsed time buffer size is neither 2 nor 0.
|
||||||
|
/// \return instance o the option.
|
||||||
|
static dhcp::OptionPtr
|
||||||
|
factoryElapsedTime6(dhcp::Option::Universe u,
|
||||||
|
uint16_t type,
|
||||||
|
const dhcp::OptionBuffer& buf);
|
||||||
|
|
||||||
|
/// \brief Factory function to create generic option.
|
||||||
|
///
|
||||||
|
/// This factory function creates option with specified universe,
|
||||||
|
/// type and buf. It does not have any additional logic validating
|
||||||
|
/// the buffer contents, size etc.
|
||||||
|
///
|
||||||
|
/// \param u universe (V6 or V4).
|
||||||
|
/// \param type option-type (ignored).
|
||||||
|
/// \param buf option-buffer.
|
||||||
|
/// \return instance o the option.
|
||||||
|
static dhcp::OptionPtr factoryGeneric(dhcp::Option::Universe u,
|
||||||
|
uint16_t type,
|
||||||
|
const dhcp::OptionBuffer& buf);
|
||||||
|
|
||||||
|
/// \brief Factory function to create IA_NA option.
|
||||||
|
///
|
||||||
|
/// This factory function creates DHCPv6 IA_NA option instance.
|
||||||
|
///
|
||||||
|
/// \todo add support for IA Address options.
|
||||||
|
///
|
||||||
|
/// \param u universe (ignored).
|
||||||
|
/// \param type option-type (ignored).
|
||||||
|
/// \param buf option-buffer carrying IANA suboptions.
|
||||||
|
/// \return instance of IA_NA option.
|
||||||
|
static dhcp::OptionPtr factoryIana6(dhcp::Option::Universe u,
|
||||||
|
uint16_t type,
|
||||||
|
const dhcp::OptionBuffer& buf);
|
||||||
|
|
||||||
|
/// \brief Factory function to create DHCPv6 ORO option.
|
||||||
|
///
|
||||||
|
/// This factory function creates DHCPv6 Option Request Option instance.
|
||||||
|
/// The created option will contain the following set of requested options:
|
||||||
|
/// - D6O_NAME_SERVERS
|
||||||
|
/// - D6O_DOMAIN_SEARCH
|
||||||
|
///
|
||||||
|
/// \param u universe (ignored).
|
||||||
|
/// \param type option-type (ignored).
|
||||||
|
/// \param buf option-buffer (ignored).
|
||||||
|
/// \return instance of ORO option.
|
||||||
|
static dhcp::OptionPtr
|
||||||
|
factoryOptionRequestOption6(dhcp::Option::Universe u,
|
||||||
|
uint16_t type,
|
||||||
|
const dhcp::OptionBuffer& buf);
|
||||||
|
|
||||||
|
/// \brief Factory function to create DHCPv6 RAPID_COMMIT option instance.
|
||||||
|
///
|
||||||
|
/// This factory function creates DHCPv6 RAPID_COMMIT option instance.
|
||||||
|
/// The buffer passed to this option must be empty because option does
|
||||||
|
/// not have any payload.
|
||||||
|
///
|
||||||
|
/// \param u universe (ignored).
|
||||||
|
/// \param type option-type (ignored).
|
||||||
|
/// \param buf option-buffer (ignored).
|
||||||
|
/// \return instance of RAPID_COMMIT option..
|
||||||
|
static dhcp::OptionPtr factoryRapidCommit6(dhcp::Option::Universe u,
|
||||||
|
uint16_t type,
|
||||||
|
const dhcp::OptionBuffer& buf);
|
||||||
|
|
||||||
|
|
||||||
|
/// \brief Factory function to create DHCPv4 Request List option.
|
||||||
|
///
|
||||||
|
/// This factory function creayes DHCPv4 PARAMETER_REQUEST_LIST option
|
||||||
|
/// instance with the following set of requested options:
|
||||||
|
/// - DHO_SUBNET_MASK,
|
||||||
|
/// - DHO_BROADCAST_ADDRESS,
|
||||||
|
/// - DHO_TIME_OFFSET,
|
||||||
|
/// - DHO_ROUTERS,
|
||||||
|
/// - DHO_DOMAIN_NAME,
|
||||||
|
/// - DHO_DOMAIN_NAME_SERVERS,
|
||||||
|
/// - DHO_HOST_NAME.
|
||||||
|
///
|
||||||
|
/// \param u universe (ignored).
|
||||||
|
/// \param type option-type (ignored).
|
||||||
|
/// \param buf option-buffer (ignored).
|
||||||
|
/// \return instance o the generic option.
|
||||||
|
static dhcp::OptionPtr factoryRequestList4(dhcp::Option::Universe u,
|
||||||
|
uint16_t type,
|
||||||
|
const dhcp::OptionBuffer& buf);
|
||||||
|
|
||||||
|
/// \brief Generate DUID.
|
||||||
|
///
|
||||||
|
/// Method generates unique DUID. The number of DUIDs it can generate
|
||||||
|
/// depends on the number of simulated clients, which is specified
|
||||||
|
/// from the command line. It uses \ref CommandOptions object to retrieve
|
||||||
|
/// number of clients. Since the last six octets of DUID are constructed
|
||||||
|
/// from the MAC address, this function uses \ref generateMacAddress
|
||||||
|
/// internally to randomize the DUID.
|
||||||
|
///
|
||||||
|
/// \todo add support for other types of DUID.
|
||||||
|
///
|
||||||
|
/// \param [out] randomized number of bytes randomized (initial value
|
||||||
|
/// is ignored).
|
||||||
|
/// \throw isc::BadValue if \ref generateMacAddress throws.
|
||||||
|
/// \return vector representing DUID.
|
||||||
|
std::vector<uint8_t> generateDuid(uint8_t& randomized) const;
|
||||||
|
|
||||||
|
/// \brief Generate MAC address.
|
||||||
|
///
|
||||||
|
/// This method generates MAC address. The number of unique
|
||||||
|
/// MAC addresses it can generate is determined by the number
|
||||||
|
/// simulated DHCP clients specified from command line. It uses
|
||||||
|
/// \ref CommandOptions object to retrieve number of clients.
|
||||||
|
/// Based on this the random value is generated and added to
|
||||||
|
/// the MAC address template (default MAC address).
|
||||||
|
///
|
||||||
|
/// \param [out] randomized number of bytes randomized (initial
|
||||||
|
/// value is ignored).
|
||||||
|
/// \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;
|
||||||
|
|
||||||
|
/// \brief generate transaction id.
|
||||||
|
///
|
||||||
|
/// Generate transaction id value (32-bit for DHCPv4,
|
||||||
|
/// 24-bit for DHCPv6).
|
||||||
|
///
|
||||||
|
/// \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
|
||||||
|
/// as possible to satisfy expected rate. Calculation used here
|
||||||
|
/// is based on current time, due time calculated with
|
||||||
|
/// \ref updateSendDue function and expected rate.
|
||||||
|
///
|
||||||
|
/// \return number of exchanges to be started immediately.
|
||||||
|
uint64_t getNextExchangesNum() const;
|
||||||
|
|
||||||
|
/// \brief Return template buffer.
|
||||||
|
///
|
||||||
|
/// Method returns template buffer at specified index.
|
||||||
|
///
|
||||||
|
/// \param idx index of template buffer.
|
||||||
|
/// \throw isc::OutOfRange if buffer index out of bounds.
|
||||||
|
/// \return reference to template buffer.
|
||||||
|
TemplateBuffer getTemplateBuffer(const size_t idx) const;
|
||||||
|
|
||||||
|
/// \brief Reads packet templates from files.
|
||||||
|
///
|
||||||
|
/// Method iterates through all specified template files, reads
|
||||||
|
/// their content and stores it in class internal buffers. Template
|
||||||
|
/// file names are specified from the command line with -T option.
|
||||||
|
///
|
||||||
|
/// \throw isc::BadValue if any of the template files does not exist
|
||||||
|
void initPacketTemplates();
|
||||||
|
|
||||||
|
/// \brief Initializes Statistics Manager.
|
||||||
|
///
|
||||||
|
/// This function initializes Statistics Manager. If there is
|
||||||
|
/// the one initialized already it is released.
|
||||||
|
void initializeStatsMgr();
|
||||||
|
|
||||||
|
/// \brief Open socket to communicate with DHCP server.
|
||||||
|
///
|
||||||
|
/// Method opens socket and binds it to local address. Function will
|
||||||
|
/// use either interface name, local address or server address
|
||||||
|
/// to create a socket, depending on what is available (specified
|
||||||
|
/// from the command line). If socket can't be created for any
|
||||||
|
/// reason, exception is thrown.
|
||||||
|
/// If destination address is broadcast (for DHCPv4) or multicast
|
||||||
|
/// (for DHCPv6) than broadcast or multicast option is set on
|
||||||
|
/// the socket. Opened socket is registered and managed by IfaceMgr.
|
||||||
|
///
|
||||||
|
/// \throw isc::BadValue if socket can't be created for given
|
||||||
|
/// interface, local address or remote address.
|
||||||
|
/// \throw isc::InvalidOperation if broadcast option can't be
|
||||||
|
/// set for the v4 socket or if multicast option cat't be set
|
||||||
|
/// for the v6 socket.
|
||||||
|
/// \throw isc::Unexpected if interal unexpected error occured.
|
||||||
|
/// \return socket descriptor.
|
||||||
|
int openSocket() const;
|
||||||
|
|
||||||
|
/// \brief Print intermediate statistics.
|
||||||
|
///
|
||||||
|
/// Print brief statistics regarding number of sent packets,
|
||||||
|
/// received packets and dropped packets so far.
|
||||||
|
void printIntermediateStats();
|
||||||
|
|
||||||
|
/// \brief Print rate statistics.
|
||||||
|
///
|
||||||
|
/// Method print packet exchange rate statistics.
|
||||||
|
void printRate() const;
|
||||||
|
|
||||||
|
/// \brief Print performance statistics.
|
||||||
|
///
|
||||||
|
/// Method prints performance statistics.
|
||||||
|
/// \throws isc::InvalidOperation if Statistics Manager was
|
||||||
|
/// not initialized.
|
||||||
|
void printStats() const;
|
||||||
|
|
||||||
|
/// \brief Process received DHCPv4 packet.
|
||||||
|
///
|
||||||
|
/// Method performs processing of the received DHCPv4 packet,
|
||||||
|
/// updates statistics and responds to the server if required,
|
||||||
|
/// e.g. when OFFER packet arrives, this function will initiate
|
||||||
|
/// REQUEST message to the server.
|
||||||
|
///
|
||||||
|
/// \warning this method does not check if provided socket is
|
||||||
|
/// valid (specifically if v4 socket for received v4 packet).
|
||||||
|
///
|
||||||
|
/// \param [in] socket socket to be used.
|
||||||
|
/// \param [in] pkt4 object representing DHCPv4 packet received.
|
||||||
|
/// \throw isc::BadValue if unknown message type received.
|
||||||
|
/// \throw isc::Unexpected if unexpected error occured.
|
||||||
|
void processReceivedPacket4(const TestControlSocket& socket,
|
||||||
|
const dhcp::Pkt4Ptr& pkt4);
|
||||||
|
|
||||||
|
/// \brief Process received DHCPv6 packet.
|
||||||
|
///
|
||||||
|
/// Method performs processing of the received 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.
|
||||||
|
///
|
||||||
|
/// \warning this method does not check if provided socket is
|
||||||
|
/// valid (specifically if v4 socket for received v4 packet).
|
||||||
|
///
|
||||||
|
/// \param [in] socket socket to be used.
|
||||||
|
/// \param [in] pkt6 object representing DHCPv6 packet received.
|
||||||
|
/// \throw isc::BadValue if unknown message type received.
|
||||||
|
/// \throw isc::Unexpected if unexpected error occured.
|
||||||
|
void processReceivedPacket6(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.
|
||||||
|
/// This function will call \ref receivePacket4 or
|
||||||
|
/// \ref receivePacket6 depending if DHCPv4 or DHCPv6 packet
|
||||||
|
/// has arrived.
|
||||||
|
///
|
||||||
|
/// \warning this method does not check if provided socket is
|
||||||
|
/// valid. Ensure that it is valid prior to calling it.
|
||||||
|
///
|
||||||
|
/// \param socket socket to be used.
|
||||||
|
/// \throw isc::BadValue if unknown message type received.
|
||||||
|
/// \throw isc::Unexpected if unexpected error occured.
|
||||||
|
void receivePackets(const TestControlSocket& socket);
|
||||||
|
|
||||||
|
/// \brief Register option factory functions for DHCPv4
|
||||||
|
///
|
||||||
|
/// Method registers option factory functions for DHCPv4.
|
||||||
|
/// These functions are called to create instances of DHCPv4
|
||||||
|
/// options. Call \ref dhcp::Option::factory to invoke factory
|
||||||
|
/// function for particular option. Don't use this function directly.
|
||||||
|
/// Use \ref registerOptionFactories instead.
|
||||||
|
void registerOptionFactories4() const;
|
||||||
|
|
||||||
|
/// \brief Register option factory functions for DHCPv6
|
||||||
|
///
|
||||||
|
/// Method registers option factory functions for DHCPv6.
|
||||||
|
/// These functions are called to create instances of DHCPv6
|
||||||
|
/// options. Call \ref dhcp::Option::factory to invoke factory
|
||||||
|
/// function for particular option. Don't use this function directly.
|
||||||
|
/// Use \ref registerOptionFactories instead.
|
||||||
|
void registerOptionFactories6() const;
|
||||||
|
|
||||||
|
/// \brief Register option factory functions for DHCPv4 or DHCPv6.
|
||||||
|
///
|
||||||
|
/// Method registers option factory functions for DHCPv4 or DHCPv6,
|
||||||
|
/// depending in whch mode test is currently running.
|
||||||
|
void registerOptionFactories() const;
|
||||||
|
|
||||||
|
|
||||||
|
/// \brief Resets internal state of the object.
|
||||||
|
///
|
||||||
|
/// Method resets internal state of the object. It has to be
|
||||||
|
/// called before new test is started.
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
/// \brief Send DHCPv4 DISCOVER message.
|
||||||
|
///
|
||||||
|
/// Method creates and sends DHCPv4 DISCOVER message to the server
|
||||||
|
/// with the following options:
|
||||||
|
/// - MESSAGE_TYPE set to DHCPDISCOVER
|
||||||
|
/// - PARAMETER_REQUEST_LIST with the same list of requested options
|
||||||
|
/// as described in \ref factoryRequestList4.
|
||||||
|
/// The transaction id and MAC address are randomly generated for
|
||||||
|
/// the message. Range of unique MAC addresses generated depends
|
||||||
|
/// on the number of clients specified from the command line.
|
||||||
|
/// Copy of sent packet is stored in the stats_mgr4_ object to
|
||||||
|
/// update statistics.
|
||||||
|
///
|
||||||
|
/// \param socket socket to be used to send the message.
|
||||||
|
/// \param preload preload mode, packets not included in statistics.
|
||||||
|
/// \throw isc::Unexpected if failed to create new packet instance.
|
||||||
|
/// \throw isc::BadValue if MAC address has invalid length.
|
||||||
|
void sendDiscover4(const TestControlSocket& socket,
|
||||||
|
const bool preload = false);
|
||||||
|
|
||||||
|
/// \brief Send DHCPv4 DISCOVER message from template.
|
||||||
|
///
|
||||||
|
/// Method sends DHCPv4 DISCOVER message from template. The
|
||||||
|
/// template data is exepcted to be in binary format. Provided
|
||||||
|
/// buffer is copied and parts of it are replaced with actual
|
||||||
|
/// data (e.g. MAC address, transaction id etc.).
|
||||||
|
/// Copy of sent packet is stored in the stats_mgr4_ object to
|
||||||
|
/// update statistics.
|
||||||
|
///
|
||||||
|
/// \param socket socket to be used to send the message.
|
||||||
|
/// \param template_buf buffer holding template packet.
|
||||||
|
/// \param preload preload mode, packets not included in statistics.
|
||||||
|
/// \throw isc::OutOfRange if randomization offset is out of bounds.
|
||||||
|
void sendDiscover4(const TestControlSocket& socket,
|
||||||
|
const std::vector<uint8_t>& template_buf,
|
||||||
|
const bool preload = false);
|
||||||
|
|
||||||
|
/// \brief Send DHCPv4 REQUEST message.
|
||||||
|
///
|
||||||
|
/// Method creates and sends DHCPv4 REQUEST message to the server.
|
||||||
|
/// Copy of sent packet is stored in the stats_mgr4_ object to
|
||||||
|
/// update statistics.
|
||||||
|
///
|
||||||
|
/// \param socket socket to be used to send message.
|
||||||
|
/// \param discover_pkt4 DISCOVER packet sent.
|
||||||
|
/// \param offer_pkt4 OFFER packet object.
|
||||||
|
/// \throw isc::Unexpected if unexpected error occured.
|
||||||
|
/// \throw isc::InvalidOperation if Statistics Manager has not been
|
||||||
|
/// initialized.
|
||||||
|
void sendRequest4(const TestControlSocket& socket,
|
||||||
|
const dhcp::Pkt4Ptr& discover_pkt4,
|
||||||
|
const dhcp::Pkt4Ptr& offer_pkt4);
|
||||||
|
|
||||||
|
/// \brief Send DHCPv4 REQUEST message from template.
|
||||||
|
///
|
||||||
|
/// Method sends DHCPv4 REQUEST message from template.
|
||||||
|
/// Copy of sent packet is stored in the stats_mgr4_ object to
|
||||||
|
/// update statistics.
|
||||||
|
///
|
||||||
|
/// \param socket socket to be used to send message.
|
||||||
|
/// \param template_buf buffer holding template packet.
|
||||||
|
/// \param discover_pkt4 DISCOVER packet sent.
|
||||||
|
/// \param offer_pkt4 OFFER packet received.
|
||||||
|
void sendRequest4(const TestControlSocket& socket,
|
||||||
|
const std::vector<uint8_t>& template_buf,
|
||||||
|
const dhcp::Pkt4Ptr& discover_pkt4,
|
||||||
|
const dhcp::Pkt4Ptr& offer_pkt4);
|
||||||
|
|
||||||
|
/// \brief Send DHCPv6 REQUEST message.
|
||||||
|
///
|
||||||
|
/// Method creates and sends DHCPv6 REQUEST message to the server
|
||||||
|
/// with the following options:
|
||||||
|
/// - D6O_ELAPSED_TIME
|
||||||
|
/// - D6O_CLIENTID
|
||||||
|
/// - D6O_SERVERID
|
||||||
|
/// Copy of sent packet is stored in the stats_mgr6_ object to
|
||||||
|
/// update statistics.
|
||||||
|
///
|
||||||
|
/// \param socket socket to be used to send message.
|
||||||
|
/// \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& advertise_pkt6);
|
||||||
|
|
||||||
|
/// \brief Send DHCPv6 REQUEST message from template.
|
||||||
|
///
|
||||||
|
/// Method sends DHCPv6 REQUEST message from template.
|
||||||
|
/// Copy of sent packet is stored in the stats_mgr6_ object to
|
||||||
|
/// update statistics.
|
||||||
|
///
|
||||||
|
/// \param socket socket to be used to send message.
|
||||||
|
/// \param template_buf packet template buffer.
|
||||||
|
/// \param advertise_pkt6 ADVERTISE packet object.
|
||||||
|
void sendRequest6(const TestControlSocket& socket,
|
||||||
|
const std::vector<uint8_t>& template_buf,
|
||||||
|
const dhcp::Pkt6Ptr& advertise_pkt6);
|
||||||
|
|
||||||
|
/// \brief Send DHCPv6 SOLICIT message.
|
||||||
|
///
|
||||||
|
/// Method creates and sends DHCPv6 SOLICIT message to the server
|
||||||
|
/// with the following options:
|
||||||
|
/// - D6O_ELAPSED_TIME,
|
||||||
|
/// - D6O_RAPID_COMMIT if rapid commit is requested in command line,
|
||||||
|
/// - D6O_CLIENTID,
|
||||||
|
/// - D6O_ORO (Option Request Option),
|
||||||
|
/// - D6O_IA_NA.
|
||||||
|
/// Copy of sent packet is stored in the stats_mgr6_ object to
|
||||||
|
/// update statistics.
|
||||||
|
///
|
||||||
|
/// \param socket socket to be used to send the message.
|
||||||
|
/// \param preload mode, packets not included in statistics.
|
||||||
|
/// \throw isc::Unexpected if failed to create new packet instance.
|
||||||
|
void sendSolicit6(const TestControlSocket& socket,
|
||||||
|
const bool preload = false);
|
||||||
|
|
||||||
|
/// \brief Send DHCPv6 SOLICIT message from template.
|
||||||
|
///
|
||||||
|
/// Method sends DHCPv6 SOLICIT message from template.
|
||||||
|
/// Copy of sent packet is stored in the stats_mgr6_ object to
|
||||||
|
/// update statistics.
|
||||||
|
///
|
||||||
|
/// \param socket socket to be used to send the message.
|
||||||
|
/// \param template_buf packet template buffer.
|
||||||
|
/// \param preload mode, packets not included in statistics.
|
||||||
|
void sendSolicit6(const TestControlSocket& socket,
|
||||||
|
const std::vector<uint8_t>& template_buf,
|
||||||
|
const bool preload = false);
|
||||||
|
|
||||||
|
/// \brief Set default DHCPv4 packet parameters.
|
||||||
|
///
|
||||||
|
/// This method sets default parameters on the DHCPv4 packet:
|
||||||
|
/// - interface name,
|
||||||
|
/// - local port = 68 (DHCP client port),
|
||||||
|
/// - remote port = 67 (DHCP server port),
|
||||||
|
/// - server's address,
|
||||||
|
/// - GIADDR = local address where socket is bound to,
|
||||||
|
/// - hops = 1 (pretending that we are a relay)
|
||||||
|
///
|
||||||
|
/// \param socket socket used to send the packet.
|
||||||
|
/// \param pkt reference to packet to be configured.
|
||||||
|
void setDefaults4(const TestControlSocket& socket,
|
||||||
|
const dhcp::Pkt4Ptr& pkt);
|
||||||
|
|
||||||
|
/// \brief Set default DHCPv6 packet parameters.
|
||||||
|
///
|
||||||
|
/// This method sets default parameters on the DHCPv6 packet:
|
||||||
|
/// - interface name,
|
||||||
|
/// - interface index,
|
||||||
|
/// - local port,
|
||||||
|
/// - remote port,
|
||||||
|
/// - local address,
|
||||||
|
/// - remote address (server).
|
||||||
|
///
|
||||||
|
/// \param socket socket used to send the packet.
|
||||||
|
/// \param pkt reference to packet to be configured.
|
||||||
|
void setDefaults6(const TestControlSocket& socket,
|
||||||
|
const dhcp::Pkt6Ptr& pkt);
|
||||||
|
|
||||||
|
/// \brief Find if diagnostic flag has been set.
|
||||||
|
///
|
||||||
|
/// \param diag diagnostic flag (a,e,i,s,r,t,T).
|
||||||
|
/// \return true if diagnostics flag has been set.
|
||||||
|
bool testDiags(const char diag) const;
|
||||||
|
|
||||||
|
/// \brief Update due time to initiate next chunk of exchanges.
|
||||||
|
///
|
||||||
|
/// Method updates due time to initiate next chunk of exchanges.
|
||||||
|
/// Function takes current time, last sent packet's time and
|
||||||
|
/// expected rate in its calculations.
|
||||||
|
void updateSendDue();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// \brief Convert binary value to hex string.
|
||||||
|
///
|
||||||
|
/// \todo Consider moving this function to src/lib/util.
|
||||||
|
///
|
||||||
|
/// \param b byte to convert.
|
||||||
|
/// \return hex string.
|
||||||
|
std::string byte2Hex(const uint8_t b) const;
|
||||||
|
|
||||||
|
/// \brief Calculate elapsed time between two packets.
|
||||||
|
///
|
||||||
|
/// \param T Pkt4Ptr or Pkt6Ptr class.
|
||||||
|
/// \param pkt1 first packet.
|
||||||
|
/// \param pkt2 second packet.
|
||||||
|
/// \throw InvalidOperation if packet timestamps are invalid.
|
||||||
|
/// \return elapsed time in milliseconds between pkt1 and pkt2.
|
||||||
|
template<class T>
|
||||||
|
uint32_t getElapsedTime(const T& pkt1, const T& pkt2);
|
||||||
|
|
||||||
|
/// \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;
|
||||||
|
|
||||||
|
/// \brief Handle interrupt signal.
|
||||||
|
///
|
||||||
|
/// Function sets flag indicating that program has been
|
||||||
|
/// interupted.
|
||||||
|
///
|
||||||
|
/// \param sig signal (ignored)
|
||||||
|
static void handleInterrupt(int sig);
|
||||||
|
|
||||||
|
/// \brief Print main diagnostics data.
|
||||||
|
///
|
||||||
|
/// Method prints main diagnostics data.
|
||||||
|
void printDiagnostics() const;
|
||||||
|
|
||||||
|
/// \brief Read DHCP message template from file.
|
||||||
|
///
|
||||||
|
/// Method reads DHCP message template from file and
|
||||||
|
/// converts it to binary format. Read data is appended
|
||||||
|
/// to template_buffers_ vector.
|
||||||
|
void readPacketTemplate(const std::string& file_name);
|
||||||
|
|
||||||
|
/// \brief Convert vector in hexadecimal string.
|
||||||
|
///
|
||||||
|
/// \todo Consider moving this function to src/lib/util.
|
||||||
|
///
|
||||||
|
/// \param vec vector to be converted.
|
||||||
|
/// \param separator separator.
|
||||||
|
std::string vector2Hex(const std::vector<uint8_t>& vec,
|
||||||
|
const std::string& separator = "") 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.
|
||||||
|
|
||||||
|
boost::posix_time::ptime last_report_; ///< Last intermediate report time.
|
||||||
|
|
||||||
|
StatsMgr4Ptr stats_mgr4_; ///< Statistics Manager 4.
|
||||||
|
StatsMgr6Ptr stats_mgr6_; ///< Statistics Manager 6.
|
||||||
|
|
||||||
|
NumberGeneratorPtr transid_gen_; ///< Transaction id generator.
|
||||||
|
NumberGeneratorPtr macaddr_gen_; ///< Numbers generator for MAC address.
|
||||||
|
|
||||||
|
/// Buffer holiding server id received in first packet
|
||||||
|
dhcp::OptionBuffer first_packet_serverid_;
|
||||||
|
|
||||||
|
/// Packet template buffers.
|
||||||
|
TemplateBufferCollection template_buffers_;
|
||||||
|
|
||||||
|
static bool interrupted_; ///< Is program interrupted.
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace perfdhcp
|
||||||
|
} // namespace isc
|
||||||
|
|
||||||
|
#endif // __COMMAND_OPTIONS_H
|
@@ -22,10 +22,13 @@ run_unittests_SOURCES += perf_pkt6_unittest.cc
|
|||||||
run_unittests_SOURCES += perf_pkt4_unittest.cc
|
run_unittests_SOURCES += perf_pkt4_unittest.cc
|
||||||
run_unittests_SOURCES += localized_option_unittest.cc
|
run_unittests_SOURCES += localized_option_unittest.cc
|
||||||
run_unittests_SOURCES += stats_mgr_unittest.cc
|
run_unittests_SOURCES += stats_mgr_unittest.cc
|
||||||
|
run_unittests_SOURCES += test_control_unittest.cc
|
||||||
|
run_unittests_SOURCES += command_options_helper.h
|
||||||
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/command_options.cc
|
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/command_options.cc
|
||||||
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/pkt_transform.cc
|
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/pkt_transform.cc
|
||||||
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt6.cc
|
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt6.cc
|
||||||
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt4.cc
|
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/perf_pkt4.cc
|
||||||
|
run_unittests_SOURCES += $(top_builddir)/tests/tools/perfdhcp/test_control.cc
|
||||||
|
|
||||||
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
|
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
|
||||||
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
|
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
|
||||||
|
138
tests/tools/perfdhcp/tests/command_options_helper.h
Normal file
138
tests/tools/perfdhcp/tests/command_options_helper.h
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
// Copyright (C) 2012 Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
//
|
||||||
|
// Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
// purpose with or without fee is hereby granted, provided that the above
|
||||||
|
// copyright notice and this permission notice appear in all copies.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||||
|
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||||
|
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
// PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
|
#ifndef __COMMAND_OPTIONS_HELPER_H
|
||||||
|
#define __COMMAND_OPTIONS_HELPER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <exceptions/exceptions.h>
|
||||||
|
#include "../command_options.h"
|
||||||
|
|
||||||
|
namespace isc {
|
||||||
|
namespace perfdhcp {
|
||||||
|
|
||||||
|
/// \brief Command Options Helper class.
|
||||||
|
///
|
||||||
|
/// This helper class can be shared between unit tests that
|
||||||
|
/// need to initialize CommandOptions objects and feed it with
|
||||||
|
/// specific command line. The command line can be given as a
|
||||||
|
/// string representing program name, options and arguments.
|
||||||
|
/// The static method exposed by this class can be used to
|
||||||
|
/// tokenize this string into array of C-strings that are later
|
||||||
|
/// consumed by \ref CommandOptions::parse. The state of the
|
||||||
|
/// CommandOptions object is reset every time the process
|
||||||
|
/// function is invoked. Also, when command line parsing is
|
||||||
|
/// ended the array of C-string is freed from the memory.
|
||||||
|
class CommandOptionsHelper {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// \brief Wrapper class for allocated argv[] array.
|
||||||
|
///
|
||||||
|
/// This class wraps allocated char** array and ensures that memory
|
||||||
|
/// allocated for this array is freed at the end o the scope.
|
||||||
|
class ArgvPtr {
|
||||||
|
public:
|
||||||
|
/// \brief Constructor.
|
||||||
|
///
|
||||||
|
/// \param argv array of C-strings.
|
||||||
|
/// \param number of C-strings in the array.
|
||||||
|
ArgvPtr(char** argv, int argc) : argv_(argv), argc_(argc) { }
|
||||||
|
|
||||||
|
/// \brief Destructor.
|
||||||
|
///
|
||||||
|
/// Dealocates wrapped array of C-strings.
|
||||||
|
~ArgvPtr() {
|
||||||
|
if (argv_ != NULL) {
|
||||||
|
for(int i = 0; i < argc_; ++i) {
|
||||||
|
free(argv_[i]);
|
||||||
|
argv_[i] = NULL;
|
||||||
|
}
|
||||||
|
free(argv_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \brief Return the array of C-strings.
|
||||||
|
///
|
||||||
|
/// \return array of C-strings.
|
||||||
|
char** getArgv() const { return (argv_); }
|
||||||
|
|
||||||
|
/// \brief Return C-strings counter.
|
||||||
|
///
|
||||||
|
/// \return C-strings counter.
|
||||||
|
int getArgc() const { return(argc_); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
char** argv_; ///< array of C-strings being wrapped.
|
||||||
|
int argc_; ///< number of C-strings.
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \brief Parse command line provided as string.
|
||||||
|
///
|
||||||
|
/// Method transforms the string representing command line
|
||||||
|
/// to the array of C-strings consumed by the
|
||||||
|
/// \ref CommandOptions::parse function and performs
|
||||||
|
/// parsing.
|
||||||
|
///
|
||||||
|
/// \param cmdline command line provided as single string.
|
||||||
|
static void process(const std::string& cmdline) {
|
||||||
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
|
int argc = 0;
|
||||||
|
char** argv = tokenizeString(cmdline, argc);
|
||||||
|
ArgvPtr args(argv, argc);
|
||||||
|
opt.reset();
|
||||||
|
opt.parse(args.getArgc(), args.getArgv());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// \brief Split string to the array of C-strings.
|
||||||
|
///
|
||||||
|
/// \param text_to_split string to be splited.
|
||||||
|
/// \param [out] num number of substrings returned.
|
||||||
|
/// \return array of C-strings created from split.
|
||||||
|
static char** tokenizeString(const std::string& text_to_split, int& num) {
|
||||||
|
char** results = NULL;
|
||||||
|
// Tokenization with std streams
|
||||||
|
std::stringstream text_stream(text_to_split);
|
||||||
|
// Iterators to be used for tokenization
|
||||||
|
std::istream_iterator<std::string> text_iterator(text_stream);
|
||||||
|
std::istream_iterator<std::string> text_end;
|
||||||
|
// Tokenize string (space is a separator) using begin and end iteratos
|
||||||
|
std::vector<std::string> tokens(text_iterator, text_end);
|
||||||
|
|
||||||
|
if (tokens.size() > 0) {
|
||||||
|
// Allocate array of C-strings where we will store tokens
|
||||||
|
results = static_cast<char**>(malloc(tokens.size() * sizeof(char*)));
|
||||||
|
if (results == NULL) {
|
||||||
|
isc_throw(Unexpected, "unable to allocate array of c-strings");
|
||||||
|
}
|
||||||
|
// Store tokens in C-strings array
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
// Return number of tokens to calling function
|
||||||
|
num = tokens.size();
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace isc::perfdhcp
|
||||||
|
} // namespace isc
|
||||||
|
|
||||||
|
#endif // __COMMAND_OPTIONS_HELPER_H
|
@@ -16,14 +16,17 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||||
|
|
||||||
|
#include <dhcp/iface_mgr.h>
|
||||||
|
#include <exceptions/exceptions.h>
|
||||||
|
|
||||||
#include "../command_options.h"
|
#include "../command_options.h"
|
||||||
|
|
||||||
#include "exceptions/exceptions.h"
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace isc;
|
using namespace isc;
|
||||||
using namespace isc::perfdhcp;
|
using namespace isc::perfdhcp;
|
||||||
|
using namespace boost::posix_time;
|
||||||
|
|
||||||
/// \brief Test Fixture Class
|
/// \brief Test Fixture Class
|
||||||
///
|
///
|
||||||
@@ -62,7 +65,7 @@ protected:
|
|||||||
/// Check if initialized values are correct
|
/// Check if initialized values are correct
|
||||||
void checkDefaults() {
|
void checkDefaults() {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp");
|
process("perfdhcp 192.168.0.1");
|
||||||
EXPECT_EQ(4, opt.getIpVersion());
|
EXPECT_EQ(4, opt.getIpVersion());
|
||||||
EXPECT_EQ(CommandOptions::DORA_SARR, opt.getExchangeMode());
|
EXPECT_EQ(CommandOptions::DORA_SARR, opt.getExchangeMode());
|
||||||
EXPECT_EQ(0, opt.getRate());
|
EXPECT_EQ(0, opt.getRate());
|
||||||
@@ -70,11 +73,45 @@ protected:
|
|||||||
EXPECT_EQ(0, opt.getClientsNum());
|
EXPECT_EQ(0, opt.getClientsNum());
|
||||||
|
|
||||||
// default mac
|
// default mac
|
||||||
uint8_t mac[6] = { 0x00, 0x0C, 0x01, 0x02, 0x03, 0x04 };
|
const uint8_t mac[6] = { 0x00, 0x0C, 0x01, 0x02, 0x03, 0x04 };
|
||||||
std::vector<uint8_t> v1 = opt.getMacPrefix();
|
std::vector<uint8_t> v1 = opt.getMacTemplate();
|
||||||
ASSERT_EQ(6, v1.size());
|
ASSERT_EQ(6, v1.size());
|
||||||
EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac));
|
EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac));
|
||||||
|
|
||||||
|
// Check if DUID is initialized. The DUID-LLT is expected
|
||||||
|
// to start with DUID_LLT value of 1 and hardware ethernet
|
||||||
|
// type equal to 1 (HWETHER_TYPE).
|
||||||
|
const uint8_t duid_llt_and_hw[4] = { 0x0, 0x1, 0x0, 0x1 };
|
||||||
|
// We assume DUID-LLT length 14. This includes 4 octets of
|
||||||
|
// DUID_LLT value, two octets of hardware type, 4 octets
|
||||||
|
// of time value and 6 octets of variable link layer (MAC)
|
||||||
|
// address.
|
||||||
|
const int duid_llt_size = 14;
|
||||||
|
// DUID is not given from the command line but it is supposed
|
||||||
|
// to be initialized by the CommandOptions private method
|
||||||
|
// generateDuidTemplate().
|
||||||
|
std::vector<uint8_t> v2 = opt.getDuidTemplate();
|
||||||
|
ASSERT_EQ(duid_llt_size, opt.getDuidTemplate().size());
|
||||||
|
EXPECT_TRUE(std::equal(v2.begin(), v2.begin() + 4,
|
||||||
|
duid_llt_and_hw));
|
||||||
|
// Check time field contents.
|
||||||
|
ptime now = microsec_clock::universal_time();
|
||||||
|
ptime duid_epoch(from_iso_string("20000101T000000"));
|
||||||
|
time_period period(duid_epoch, now);
|
||||||
|
uint32_t duration_sec = period.length().total_seconds();
|
||||||
|
// Read time from the template generated.
|
||||||
|
uint32_t duration_from_template = 0;
|
||||||
|
memcpy(&duration_from_template, &v2[4], 4);
|
||||||
|
duration_from_template = htonl(duration_from_template);
|
||||||
|
// In special cases, we may have overflow in time field
|
||||||
|
// so we give ourselves the margin of 10 seconds here.
|
||||||
|
// If time value has been set more then 10 seconds back
|
||||||
|
// it is safe to compare it with the time value generated
|
||||||
|
// from now.
|
||||||
|
if (duration_from_template > 10) {
|
||||||
|
EXPECT_GE(duration_sec, duration_from_template);
|
||||||
|
}
|
||||||
|
|
||||||
EXPECT_EQ(0, opt.getBase().size());
|
EXPECT_EQ(0, opt.getBase().size());
|
||||||
EXPECT_EQ(0, opt.getNumRequests().size());
|
EXPECT_EQ(0, opt.getNumRequests().size());
|
||||||
EXPECT_EQ(0, opt.getPeriod());
|
EXPECT_EQ(0, opt.getPeriod());
|
||||||
@@ -104,7 +141,7 @@ protected:
|
|||||||
EXPECT_GT(0, opt.getRequestedIpOffset());
|
EXPECT_GT(0, opt.getRequestedIpOffset());
|
||||||
EXPECT_EQ("", opt.getDiags());
|
EXPECT_EQ("", opt.getDiags());
|
||||||
EXPECT_EQ("", opt.getWrapped());
|
EXPECT_EQ("", opt.getWrapped());
|
||||||
EXPECT_EQ("", opt.getServerName());
|
EXPECT_EQ("192.168.0.1", opt.getServerName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \brief Split string to array of C-strings
|
/// \brief Split string to array of C-strings
|
||||||
@@ -145,153 +182,210 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, Defaults) {
|
TEST_F(CommandOptionsTest, Defaults) {
|
||||||
process("perfdhcp");
|
process("perfdhcp all");
|
||||||
checkDefaults();
|
checkDefaults();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, UseFirst) {
|
TEST_F(CommandOptionsTest, UseFirst) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -1 -B -l ethx");
|
process("perfdhcp -1 -B -l ethx all");
|
||||||
EXPECT_TRUE(opt.isUseFirst());
|
EXPECT_TRUE(opt.isUseFirst());
|
||||||
}
|
}
|
||||||
TEST_F(CommandOptionsTest, IpVersion) {
|
TEST_F(CommandOptionsTest, IpVersion) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -6 -l ethx -c -i");
|
process("perfdhcp -6 -l ethx -c -i all");
|
||||||
EXPECT_EQ(6, opt.getIpVersion());
|
EXPECT_EQ(6, opt.getIpVersion());
|
||||||
EXPECT_EQ("ethx", opt.getLocalName());
|
EXPECT_EQ("ethx", opt.getLocalName());
|
||||||
EXPECT_TRUE(opt.isRapidCommit());
|
EXPECT_TRUE(opt.isRapidCommit());
|
||||||
EXPECT_FALSE(opt.isBroadcast());
|
EXPECT_FALSE(opt.isBroadcast());
|
||||||
process("perfdhcp -4 -B -l ethx");
|
process("perfdhcp -4 -B -l ethx all");
|
||||||
EXPECT_EQ(4, opt.getIpVersion());
|
EXPECT_EQ(4, opt.getIpVersion());
|
||||||
EXPECT_TRUE(opt.isBroadcast());
|
EXPECT_TRUE(opt.isBroadcast());
|
||||||
EXPECT_FALSE(opt.isRapidCommit());
|
EXPECT_FALSE(opt.isRapidCommit());
|
||||||
|
|
||||||
// Negative test cases
|
// Negative test cases
|
||||||
// -4 and -6 must not coexist
|
// -4 and -6 must not coexist
|
||||||
EXPECT_THROW(process("perfdhcp -4 -6 -l ethx"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -4 -6 -l ethx all"), isc::InvalidParameter);
|
||||||
// -6 and -B must not coexist
|
// -6 and -B must not coexist
|
||||||
EXPECT_THROW(process("perfdhcp -6 -B -l ethx"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -6 -B -l ethx all"), isc::InvalidParameter);
|
||||||
// -c and -4 (default) must not coexist
|
// -c and -4 (default) must not coexist
|
||||||
EXPECT_THROW(process("perfdhcp -c -l ethx"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -c -l ethx all"), isc::InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, Rate) {
|
TEST_F(CommandOptionsTest, Rate) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -4 -r 10 -l ethx");
|
process("perfdhcp -4 -r 10 -l ethx all");
|
||||||
EXPECT_EQ(10, opt.getRate());
|
EXPECT_EQ(10, opt.getRate());
|
||||||
|
|
||||||
// Negative test cases
|
// Negative test cases
|
||||||
// Rate must not be 0
|
// Rate must not be 0
|
||||||
EXPECT_THROW(process("perfdhcp -4 -r 0 -l ethx"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -4 -r 0 -l ethx all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
// -r must be specified to use -n, -p and -D
|
// -r must be specified to use -n, -p and -D
|
||||||
EXPECT_THROW(process("perfdhcp -6 -t 5 -l ethx"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -6 -t 5 -l ethx all"),
|
||||||
EXPECT_THROW(process("perfdhcp -4 -n 150 -l ethx"), isc::InvalidParameter);
|
isc::InvalidParameter);
|
||||||
EXPECT_THROW(process("perfdhcp -6 -p 120 -l ethx"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -4 -n 150 -l ethx all"),
|
||||||
EXPECT_THROW(process("perfdhcp -4 -D 1400 -l ethx"), isc::InvalidParameter);
|
isc::InvalidParameter);
|
||||||
|
EXPECT_THROW(process("perfdhcp -6 -p 120 -l ethx all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
|
EXPECT_THROW(process("perfdhcp -4 -D 1400 -l ethx all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, ReportDelay) {
|
TEST_F(CommandOptionsTest, ReportDelay) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -r 100 -t 17 -l ethx");
|
process("perfdhcp -r 100 -t 17 -l ethx all");
|
||||||
EXPECT_EQ(17, opt.getReportDelay());
|
EXPECT_EQ(17, opt.getReportDelay());
|
||||||
|
|
||||||
// Negative test cases
|
// Negative test cases
|
||||||
// -t must be positive integer
|
// -t must be positive integer
|
||||||
EXPECT_THROW(process("perfdhcp -r 10 -t -8 -l ethx"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -r 10 -t -8 -l ethx all"),
|
||||||
EXPECT_THROW(process("perfdhcp -r 10 -t 0 -l ethx"), isc::InvalidParameter);
|
isc::InvalidParameter);
|
||||||
EXPECT_THROW(process("perfdhcp -r 10 -t s -l ethx"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -r 10 -t 0 -l ethx all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
|
EXPECT_THROW(process("perfdhcp -r 10 -t s -l ethx all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, ClientsNum) {
|
TEST_F(CommandOptionsTest, ClientsNum) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -R 200 -l ethx");
|
process("perfdhcp -R 200 -l ethx all");
|
||||||
EXPECT_EQ(200, opt.getClientsNum());
|
EXPECT_EQ(200, opt.getClientsNum());
|
||||||
process("perfdhcp -R 0 -l ethx");
|
process("perfdhcp -R 0 -l ethx all");
|
||||||
EXPECT_EQ(0, opt.getClientsNum());
|
EXPECT_EQ(0, opt.getClientsNum());
|
||||||
|
|
||||||
// Negative test cases
|
// Negative test cases
|
||||||
// Number of clients must be non-negative integer
|
// Number of clients must be non-negative integer
|
||||||
EXPECT_THROW(process("perfdhcp -R -5 -l ethx"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -R -5 -l ethx all"),
|
||||||
EXPECT_THROW(process("perfdhcp -R gs -l ethx"), isc::InvalidParameter);
|
isc::InvalidParameter);
|
||||||
|
EXPECT_THROW(process("perfdhcp -R gs -l ethx all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, Base) {
|
TEST_F(CommandOptionsTest, Base) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -6 -b MAC=10::20::30::40::50::60 -l ethx -b duiD=1AB7F5670901FF");
|
|
||||||
uint8_t mac[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60 };
|
uint8_t mac[6] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60 };
|
||||||
uint8_t duid[7] = { 0x1A, 0xB7, 0xF5, 0x67, 0x09, 0x01, 0xFF };
|
uint8_t duid[14] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
|
||||||
|
0x01, 0x01, 0x01, 0x10, 0x11, 0x1F, 0x14 };
|
||||||
// Test Mac
|
// Test DUID and MAC together.
|
||||||
std::vector<uint8_t> v1 = opt.getMacPrefix();
|
EXPECT_NO_THROW(process("perfdhcp -b DUID=0101010101010101010110111F14"
|
||||||
ASSERT_EQ(6, v1.size());
|
" -b MAC=10::20::30::40::50::60"
|
||||||
|
" -l 127.0.0.1 all"));
|
||||||
|
std::vector<uint8_t> v1 = opt.getMacTemplate();
|
||||||
|
std::vector<uint8_t> v2 = opt.getDuidTemplate();
|
||||||
|
v2 = opt.getDuidTemplate();
|
||||||
EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac));
|
EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac));
|
||||||
// "3x" is invalid value in MAC address
|
EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid));
|
||||||
EXPECT_THROW(process("perfdhcp -b mac=10::2::3x::4::5::6 -l ethx"), isc::InvalidParameter);
|
// Test valid DUID.
|
||||||
|
EXPECT_NO_THROW(
|
||||||
|
process("perfdhcp -b duid=0101010101010101010110111F14 -l 127.0.0.1 all")
|
||||||
|
);
|
||||||
|
|
||||||
// Test DUID
|
|
||||||
std::vector<uint8_t> v2 = opt.getDuidPrefix();
|
|
||||||
ASSERT_EQ(sizeof(duid) / sizeof(uint8_t), v2.size());
|
ASSERT_EQ(sizeof(duid) / sizeof(uint8_t), v2.size());
|
||||||
EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid));
|
EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid));
|
||||||
// "t" is invalid digit in DUID
|
// Test mix of upper/lower case letters.
|
||||||
EXPECT_THROW(process("perfdhcp -6 -l ethx -b duiD=1AB7Ft670901FF"), isc::InvalidParameter);
|
EXPECT_NO_THROW(process("perfdhcp -b DuiD=0101010101010101010110111F14"
|
||||||
|
" -b Mac=10::20::30::40::50::60"
|
||||||
// Some more negative test cases
|
" -l 127.0.0.1 all"));
|
||||||
|
v1 = opt.getMacTemplate();
|
||||||
|
v2 = opt.getDuidTemplate();
|
||||||
|
EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac));
|
||||||
|
EXPECT_TRUE(std::equal(v2.begin(), v2.end(), duid));
|
||||||
|
// Use "ether" instead of "mac".
|
||||||
|
EXPECT_NO_THROW(process("perfdhcp -b ether=10::20::30::40::50::60"
|
||||||
|
" -l 127.0.0.1 all"));
|
||||||
|
v1 = opt.getMacTemplate();
|
||||||
|
EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac));
|
||||||
|
// Use "ETHER" in upper case.
|
||||||
|
EXPECT_NO_THROW(process("perfdhcp -b ETHER=10::20::30::40::50::60"
|
||||||
|
" -l 127.0.0.1 all"));
|
||||||
|
v1 = opt.getMacTemplate();
|
||||||
|
EXPECT_TRUE(std::equal(v1.begin(), v1.end(), mac));
|
||||||
|
// "t" is invalid character in DUID
|
||||||
|
EXPECT_THROW(process("perfdhcp -6 -l ethx -b "
|
||||||
|
"duid=010101010101010101t110111F14 all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
|
// "3x" is invalid value in MAC address
|
||||||
|
EXPECT_THROW(process("perfdhcp -b mac=10::2::3x::4::5::6 -l ethx all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
// Base is not specified
|
// Base is not specified
|
||||||
EXPECT_THROW(process("perfdhcp -b -l ethx"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -b -l ethx all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
// Typo: should be mac= instead of mc=
|
// Typo: should be mac= instead of mc=
|
||||||
EXPECT_THROW(process("perfdhcp -l ethx -b mc=00:01:02:03::04:05"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -l ethx -b mc=00:01:02:03::04:05 all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
|
// Too short DUID (< 6).
|
||||||
|
EXPECT_THROW(process("perfdhcp -l ethx -b duid=00010203 all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
|
// Odd number of digits.
|
||||||
|
EXPECT_THROW(process("perfdhcp -l ethx -b duid=000102030405060 all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
|
// Too short MAC (!= 6).
|
||||||
|
EXPECT_THROW(process("perfdhcp -l ethx -b mac=00:01:02:04 all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, DropTime) {
|
TEST_F(CommandOptionsTest, DropTime) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -l ethx -d 12");
|
process("perfdhcp -l ethx -d 12 all");
|
||||||
ASSERT_EQ(2, opt.getDropTime().size());
|
ASSERT_EQ(2, opt.getDropTime().size());
|
||||||
EXPECT_DOUBLE_EQ(12, opt.getDropTime()[0]);
|
EXPECT_DOUBLE_EQ(12, opt.getDropTime()[0]);
|
||||||
EXPECT_DOUBLE_EQ(1, opt.getDropTime()[1]);
|
EXPECT_DOUBLE_EQ(1, opt.getDropTime()[1]);
|
||||||
|
|
||||||
process("perfdhcp -l ethx -d 2 -d 4.7");
|
process("perfdhcp -l ethx -d 2 -d 4.7 all");
|
||||||
ASSERT_EQ(2, opt.getDropTime().size());
|
ASSERT_EQ(2, opt.getDropTime().size());
|
||||||
EXPECT_DOUBLE_EQ(2, opt.getDropTime()[0]);
|
EXPECT_DOUBLE_EQ(2, opt.getDropTime()[0]);
|
||||||
EXPECT_DOUBLE_EQ(4.7, opt.getDropTime()[1]);
|
EXPECT_DOUBLE_EQ(4.7, opt.getDropTime()[1]);
|
||||||
|
|
||||||
// Negative test cases
|
// Negative test cases
|
||||||
// Drop time must not be negative
|
// Drop time must not be negative
|
||||||
EXPECT_THROW(process("perfdhcp -l ethx -d -2 -d 4.7"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -l ethx -d -2 -d 4.7 all"),
|
||||||
EXPECT_THROW(process("perfdhcp -l ethx -d -9.1 -d 0"), isc::InvalidParameter);
|
isc::InvalidParameter);
|
||||||
|
EXPECT_THROW(process("perfdhcp -l ethx -d -9.1 -d 0 all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, TimeOffset) {
|
TEST_F(CommandOptionsTest, TimeOffset) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -l ethx -T file1.x -T file2.x -E 4");
|
process("perfdhcp -l ethx -T file1.x -T file2.x -E 4 all");
|
||||||
EXPECT_EQ(4, opt.getElapsedTimeOffset());
|
EXPECT_EQ(4, opt.getElapsedTimeOffset());
|
||||||
|
|
||||||
// Negative test cases
|
// Negative test cases
|
||||||
// Argument -E must be used with -T
|
// Argument -E must be used with -T
|
||||||
EXPECT_THROW(process("perfdhcp -l ethx -E 3 -i"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -l ethx -E 3 -i all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
// Value in -E not specified
|
// Value in -E not specified
|
||||||
EXPECT_THROW(process("perfdhcp -l ethx -T file.x -E -i"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -l ethx -T file.x -E -i all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
// Value for -E must not be negative
|
// Value for -E must not be negative
|
||||||
EXPECT_THROW(process("perfdhcp -l ethx -E -3 -T file.x"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -l ethx -E -3 -T file.x all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, ExchangeMode) {
|
TEST_F(CommandOptionsTest, ExchangeMode) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -l ethx -i");
|
process("perfdhcp -l ethx -i all");
|
||||||
EXPECT_EQ(CommandOptions::DO_SA, opt.getExchangeMode());
|
EXPECT_EQ(CommandOptions::DO_SA, opt.getExchangeMode());
|
||||||
|
|
||||||
// Negative test cases
|
// Negative test cases
|
||||||
// No template file specified
|
// No template file specified
|
||||||
EXPECT_THROW(process("perfdhcp -i -l ethx -X 3"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -i -l ethx -X 3 all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
// Offsets can't be used in simple exchanges (-i)
|
// Offsets can't be used in simple exchanges (-i)
|
||||||
EXPECT_THROW(process("perfdhcp -i -l ethx -O 2 -T file.x"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -i -l ethx -O 2 -T file.x all"),
|
||||||
EXPECT_THROW(process("perfdhcp -i -l ethx -E 3 -T file.x"), isc::InvalidParameter);
|
isc::InvalidParameter);
|
||||||
EXPECT_THROW(process("perfdhcp -i -l ethx -S 1 -T file.x"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -i -l ethx -E 3 -T file.x all"),
|
||||||
EXPECT_THROW(process("perfdhcp -i -l ethx -I 2 -T file.x"), isc::InvalidParameter);
|
isc::InvalidParameter);
|
||||||
|
EXPECT_THROW(process("perfdhcp -i -l ethx -S 1 -T file.x all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
|
EXPECT_THROW(process("perfdhcp -i -l ethx -I 2 -T file.x all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, Offsets) {
|
TEST_F(CommandOptionsTest, Offsets) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -E5 -4 -I 2 -S3 -O 30 -X7 -l ethx -X3 -T file1.x -T file2.x");
|
process("perfdhcp -E5 -4 -I 2 -S3 -O 30 -X7 -l ethx "
|
||||||
|
"-X3 -T file1.x -T file2.x all");
|
||||||
EXPECT_EQ(2, opt.getRequestedIpOffset());
|
EXPECT_EQ(2, opt.getRequestedIpOffset());
|
||||||
EXPECT_EQ(5, opt.getElapsedTimeOffset());
|
EXPECT_EQ(5, opt.getElapsedTimeOffset());
|
||||||
EXPECT_EQ(3, opt.getServerIdOffset());
|
EXPECT_EQ(3, opt.getServerIdOffset());
|
||||||
@@ -304,151 +398,237 @@ TEST_F(CommandOptionsTest, Offsets) {
|
|||||||
|
|
||||||
// Negative test cases
|
// Negative test cases
|
||||||
// IP offset/IA_NA offset must be positive
|
// IP offset/IA_NA offset must be positive
|
||||||
EXPECT_THROW(process("perfdhcp -6 -I 0 -l ethx"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -6 -I 0 -l ethx all"),
|
||||||
EXPECT_THROW(process("perfdhcp -6 -I -4 -l ethx"), isc::InvalidParameter);
|
isc::InvalidParameter);
|
||||||
|
EXPECT_THROW(process("perfdhcp -6 -I -4 -l ethx all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
|
|
||||||
// TODO - other negative cases
|
// TODO - other negative cases
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, LocalPort) {
|
TEST_F(CommandOptionsTest, LocalPort) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -l ethx -L 2000");
|
process("perfdhcp -l ethx -L 2000 all");
|
||||||
EXPECT_EQ(2000, opt.getLocalPort());
|
EXPECT_EQ(2000, opt.getLocalPort());
|
||||||
|
|
||||||
// Negative test cases
|
// Negative test cases
|
||||||
// Local port must be between 0..65535
|
// Local port must be between 0..65535
|
||||||
EXPECT_THROW(process("perfdhcp -l ethx -L -2"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -l ethx -L -2 all"),
|
||||||
EXPECT_THROW(process("perfdhcp -l ethx -L"), isc::InvalidParameter);
|
isc::InvalidParameter);
|
||||||
EXPECT_THROW(process("perfdhcp -l ethx -L 65540"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -l ethx -L all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
|
EXPECT_THROW(process("perfdhcp -l ethx -L 65540 all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, Preload) {
|
TEST_F(CommandOptionsTest, Preload) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -1 -P 3 -l ethx");
|
process("perfdhcp -1 -P 3 -l ethx all");
|
||||||
EXPECT_EQ(3, opt.getPreload());
|
EXPECT_EQ(3, opt.getPreload());
|
||||||
|
|
||||||
// Negative test cases
|
// Negative test cases
|
||||||
// Number of preload packages must not be negative integer
|
// Number of preload packages must not be negative integer
|
||||||
EXPECT_THROW(process("perfdhcp -P -1 -l ethx"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -P -1 -l ethx all"),
|
||||||
EXPECT_THROW(process("perfdhcp -P -3 -l ethx"), isc::InvalidParameter);
|
isc::InvalidParameter);
|
||||||
|
EXPECT_THROW(process("perfdhcp -P -3 -l ethx all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, Seed) {
|
TEST_F(CommandOptionsTest, Seed) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -6 -P 2 -s 23 -l ethx");
|
process("perfdhcp -6 -P 2 -s 23 -l ethx all");
|
||||||
EXPECT_EQ(23, opt.getSeed());
|
EXPECT_EQ(23, opt.getSeed());
|
||||||
EXPECT_TRUE(opt.isSeeded());
|
EXPECT_TRUE(opt.isSeeded());
|
||||||
|
|
||||||
process("perfdhcp -6 -P 2 -s 0 -l ethx");
|
process("perfdhcp -6 -P 2 -s 0 -l ethx all");
|
||||||
EXPECT_EQ(0, opt.getSeed());
|
EXPECT_EQ(0, opt.getSeed());
|
||||||
EXPECT_FALSE(opt.isSeeded());
|
EXPECT_FALSE(opt.isSeeded());
|
||||||
|
|
||||||
// Negtaive test cases
|
// Negtaive test cases
|
||||||
// Seed must be non-negative integer
|
// Seed must be non-negative integer
|
||||||
EXPECT_THROW(process("perfdhcp -6 -P 2 -s -5 -l ethx"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -6 -P 2 -s -5 -l ethx all"),
|
||||||
EXPECT_THROW(process("perfdhcp -6 -P 2 -s -l ethx"), isc::InvalidParameter);
|
isc::InvalidParameter);
|
||||||
|
EXPECT_THROW(process("perfdhcp -6 -P 2 -s -l ethx all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, TemplateFiles) {
|
TEST_F(CommandOptionsTest, TemplateFiles) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -T file1.x -l ethx");
|
process("perfdhcp -T file1.x -l ethx all");
|
||||||
ASSERT_EQ(1, opt.getTemplateFiles().size());
|
ASSERT_EQ(1, opt.getTemplateFiles().size());
|
||||||
EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]);
|
EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]);
|
||||||
|
|
||||||
process("perfdhcp -T file1.x -s 12 -w start -T file2.x -4 -l ethx");
|
process("perfdhcp -T file1.x -s 12 -w start -T file2.x -4 -l ethx all");
|
||||||
ASSERT_EQ(2, opt.getTemplateFiles().size());
|
ASSERT_EQ(2, opt.getTemplateFiles().size());
|
||||||
EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]);
|
EXPECT_EQ("file1.x", opt.getTemplateFiles()[0]);
|
||||||
EXPECT_EQ("file2.x", opt.getTemplateFiles()[1]);
|
EXPECT_EQ("file2.x", opt.getTemplateFiles()[1]);
|
||||||
|
|
||||||
// Negative test cases
|
// Negative test cases
|
||||||
// No template file specified
|
// No template file specified
|
||||||
EXPECT_THROW(process("perfdhcp -s 12 -l ethx -T"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -s 12 -T -l ethx all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
// Too many template files specified
|
// Too many template files specified
|
||||||
EXPECT_THROW(process("perfdhcp -s 12 -l ethx -T file.x -T file.x -T file.x"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -s 12 -l ethx -T file.x "
|
||||||
|
"-T file.x -T file.x all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, Wrapped) {
|
TEST_F(CommandOptionsTest, Wrapped) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -B -w start -i -l ethx");
|
process("perfdhcp -B -w start -i -l ethx all");
|
||||||
EXPECT_EQ("start", opt.getWrapped());
|
EXPECT_EQ("start", opt.getWrapped());
|
||||||
|
|
||||||
// Negative test cases
|
// Negative test cases
|
||||||
// Missing command after -w, expected start/stop
|
// Missing command after -w, expected start/stop
|
||||||
EXPECT_THROW(process("perfdhcp -B -i -l ethx -w"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -B -i -l ethx -w all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, Diagnostics) {
|
TEST_F(CommandOptionsTest, Diagnostics) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -l ethx -i -x asTe");
|
process("perfdhcp -l ethx -i -x asTe all");
|
||||||
EXPECT_EQ("asTe", opt.getDiags());
|
EXPECT_EQ("asTe", opt.getDiags());
|
||||||
|
|
||||||
// Negative test cases
|
// Negative test cases
|
||||||
// No diagnostics string specified
|
// No diagnostics string specified
|
||||||
EXPECT_THROW(process("perfdhcp -l ethx -i -x"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -l ethx -i -x all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, Aggressivity) {
|
TEST_F(CommandOptionsTest, Aggressivity) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -a 10 -l 192.168.0.1");
|
process("perfdhcp -a 10 -l 192.168.0.1 all");
|
||||||
EXPECT_EQ(10, opt.getAggressivity());
|
EXPECT_EQ(10, opt.getAggressivity());
|
||||||
|
|
||||||
// Negative test cases
|
// Negative test cases
|
||||||
// Aggressivity must be non negative integer
|
// Aggressivity must be non negative integer
|
||||||
EXPECT_THROW(process("perfdhcp -l ethx -a 0"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -l ethx -a 0 all"),
|
||||||
EXPECT_THROW(process("perfdhcp -l ethx -a"), isc::InvalidParameter);
|
isc::InvalidParameter);
|
||||||
EXPECT_THROW(process("perfdhcp -a -2 -l ethx -a 3"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -l ethx -a all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
|
EXPECT_THROW(process("perfdhcp -a -2 -l ethx -a 3 all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, MaxDrop) {
|
TEST_F(CommandOptionsTest, MaxDrop) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -D 25 -l ethx -r 10");
|
process("perfdhcp -D 25 -l ethx -r 10 all");
|
||||||
EXPECT_EQ(25, opt.getMaxDrop()[0]);
|
EXPECT_EQ(25, opt.getMaxDrop()[0]);
|
||||||
process("perfdhcp -D 25 -l ethx -D 15 -r 10");
|
process("perfdhcp -D 25 -l ethx -D 15 -r 10 all");
|
||||||
EXPECT_EQ(25, opt.getMaxDrop()[0]);
|
EXPECT_EQ(25, opt.getMaxDrop()[0]);
|
||||||
EXPECT_EQ(15, opt.getMaxDrop()[1]);
|
EXPECT_EQ(15, opt.getMaxDrop()[1]);
|
||||||
|
|
||||||
process("perfdhcp -D 15% -l ethx -r 10");
|
process("perfdhcp -D 15% -l ethx -r 10 all");
|
||||||
EXPECT_EQ(15, opt.getMaxDropPercentage()[0]);
|
EXPECT_EQ(15, opt.getMaxDropPercentage()[0]);
|
||||||
process("perfdhcp -D 15% -D25% -l ethx -r 10");
|
process("perfdhcp -D 15% -D25% -l ethx -r 10 all");
|
||||||
EXPECT_EQ(15, opt.getMaxDropPercentage()[0]);
|
EXPECT_EQ(15, opt.getMaxDropPercentage()[0]);
|
||||||
EXPECT_EQ(25, opt.getMaxDropPercentage()[1]);
|
EXPECT_EQ(25, opt.getMaxDropPercentage()[1]);
|
||||||
process("perfdhcp -D 1% -D 99% -l ethx -r 10");
|
process("perfdhcp -D 1% -D 99% -l ethx -r 10 all");
|
||||||
EXPECT_EQ(1, opt.getMaxDropPercentage()[0]);
|
EXPECT_EQ(1, opt.getMaxDropPercentage()[0]);
|
||||||
EXPECT_EQ(99, opt.getMaxDropPercentage()[1]);
|
EXPECT_EQ(99, opt.getMaxDropPercentage()[1]);
|
||||||
|
|
||||||
// Negative test cases
|
// Negative test cases
|
||||||
// Too many -D<value> options
|
// Too many -D<value> options
|
||||||
EXPECT_THROW(process("perfdhcp -D 0% -D 1 -l ethx -r20 -D 3"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -D 0% -D 1 -l ethx -r20 -D 3 all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
// Too many -D<value%> options
|
// Too many -D<value%> options
|
||||||
EXPECT_THROW(process("perfdhcp -D 99% -D 13% -l ethx -r20 -D 10%"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -D 99% -D 13% -l ethx -r20 -D 10% all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
// Percentage is out of bounds
|
// Percentage is out of bounds
|
||||||
EXPECT_THROW(process("perfdhcp -D101% -D 13% -l ethx -r20"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -D101% -D 13% -l ethx -r20 all"),
|
||||||
EXPECT_THROW(process("perfdhcp -D0% -D 13% -l ethx -r20"), isc::InvalidParameter);
|
isc::InvalidParameter);
|
||||||
|
EXPECT_THROW(process("perfdhcp -D0% -D 13% -l ethx -r20 all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, NumRequest) {
|
TEST_F(CommandOptionsTest, NumRequest) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -n 1000 -r 10 -l ethx");
|
process("perfdhcp -n 1000 -r 10 -l ethx all");
|
||||||
EXPECT_EQ(1000, opt.getNumRequests()[0]);
|
EXPECT_EQ(1000, opt.getNumRequests()[0]);
|
||||||
process("perfdhcp -n 5 -r 10 -n 500 -l ethx");
|
process("perfdhcp -n 5 -r 10 -n 500 -l ethx all");
|
||||||
EXPECT_EQ(5, opt.getNumRequests()[0]);
|
EXPECT_EQ(5, opt.getNumRequests()[0]);
|
||||||
EXPECT_EQ(500, opt.getNumRequests()[1]);
|
EXPECT_EQ(500, opt.getNumRequests()[1]);
|
||||||
|
|
||||||
// Negative test cases
|
// Negative test cases
|
||||||
// Too many -n<value> parameters, expected maximum 2
|
// Too many -n<value> parameters, expected maximum 2
|
||||||
EXPECT_THROW(process("perfdhcp -n 1 -n 2 -l ethx -n3 -r 20"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -n 1 -n 2 -l ethx -n3 -r 20 all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
// Num request must be positive integer
|
// Num request must be positive integer
|
||||||
EXPECT_THROW(process("perfdhcp -n 1 -n -22 -l ethx -r 10"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -n 1 -n -22 -l ethx -r 10 all"),
|
||||||
EXPECT_THROW(process("perfdhcp -n 0 -l ethx -r 10"), isc::InvalidParameter);
|
isc::InvalidParameter);
|
||||||
|
EXPECT_THROW(process("perfdhcp -n 0 -l ethx -r 10 all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CommandOptionsTest, Period) {
|
TEST_F(CommandOptionsTest, Period) {
|
||||||
CommandOptions& opt = CommandOptions::instance();
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
process("perfdhcp -p 120 -l ethx -r 100");
|
process("perfdhcp -p 120 -l ethx -r 100 all");
|
||||||
EXPECT_EQ(120, opt.getPeriod());
|
EXPECT_EQ(120, opt.getPeriod());
|
||||||
|
|
||||||
// Negative test cases
|
// Negative test cases
|
||||||
// Test period must be positive integer
|
// Test period must be positive integer
|
||||||
EXPECT_THROW(process("perfdhcp -p 0 -l ethx -r 50"), isc::InvalidParameter);
|
EXPECT_THROW(process("perfdhcp -p 0 -l ethx -r 50 all"),
|
||||||
EXPECT_THROW(process("perfdhcp -p -3 -l ethx -r 50"), isc::InvalidParameter);
|
isc::InvalidParameter);
|
||||||
|
EXPECT_THROW(process("perfdhcp -p -3 -l ethx -r 50 all"),
|
||||||
|
isc::InvalidParameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CommandOptionsTest, Interface) {
|
||||||
|
// In order to make this test portable we need to know
|
||||||
|
// at least one interface name on OS where test is run.
|
||||||
|
// Interface Manager has ability to detect interfaces.
|
||||||
|
// Altough we don't call initIsInterface explicitely
|
||||||
|
// here it is called by CommandOptions object interally
|
||||||
|
// so this function is covered by the test.
|
||||||
|
dhcp::IfaceMgr& iface_mgr = dhcp::IfaceMgr::instance();
|
||||||
|
const dhcp::IfaceMgr::IfaceCollection& ifaces = iface_mgr.getIfaces();
|
||||||
|
std::string iface_name;
|
||||||
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
|
// The local loopback interface should be available.
|
||||||
|
// If no interface have been found for any reason we should
|
||||||
|
// not fail this test.
|
||||||
|
if (ifaces.size() > 0) {
|
||||||
|
// Get the name of the interface we detected.
|
||||||
|
iface_name = ifaces.begin()->getName();
|
||||||
|
// Use the name in the command parser.
|
||||||
|
ASSERT_NO_THROW(process("perfdhcp -4 -l " + iface_name + " abc"));
|
||||||
|
// We expect that command parser will detect that argument
|
||||||
|
// specified along with '-l' is the interface name.
|
||||||
|
EXPECT_TRUE(opt.isInterface());
|
||||||
|
|
||||||
|
// If neither interface nor server is specified then
|
||||||
|
// exception is expected to be thrown.
|
||||||
|
EXPECT_THROW(process("perfdhcp -4"), isc::InvalidParameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CommandOptionsTest, Server) {
|
||||||
|
CommandOptions& opt = CommandOptions::instance();
|
||||||
|
// There is at least server parameter needed. If server is not
|
||||||
|
// specified the local interface must be specified.
|
||||||
|
// The server value equal to 'all' means use broadcast.
|
||||||
|
ASSERT_NO_THROW(process("perfdhcp all"));
|
||||||
|
// Once command line is parsed we expect that server name is
|
||||||
|
// set to broadcast address because 'all' was specified.
|
||||||
|
EXPECT_TRUE(opt.isBroadcast());
|
||||||
|
// The broadcast address is 255.255.255.255.
|
||||||
|
EXPECT_EQ("255.255.255.255", opt.getServerName());
|
||||||
|
|
||||||
|
// When all is specified for DHCPv6 mode we expect
|
||||||
|
// FF02::1:2 as a server name which means All DHCP
|
||||||
|
// servers and relay agents in local network segment
|
||||||
|
ASSERT_NO_THROW(process("perfdhcp -6 all"));
|
||||||
|
EXPECT_EQ("FF02::1:2", opt.getServerName());
|
||||||
|
|
||||||
|
// When server='servers' in DHCPv6 mode we expect
|
||||||
|
// FF05::1:3 as server name which means All DHCP
|
||||||
|
// servers in local network.
|
||||||
|
ASSERT_NO_THROW(process("perfdhcp -6 servers"));
|
||||||
|
EXPECT_EQ("FF05::1:3", opt.getServerName());
|
||||||
|
|
||||||
|
// If server name is neither 'all' nor 'servers'
|
||||||
|
// the given argument value is expected to be
|
||||||
|
// returned.
|
||||||
|
ASSERT_NO_THROW(process("perfdhcp -6 abc"));
|
||||||
|
EXPECT_EQ("abc", opt.getServerName());
|
||||||
}
|
}
|
||||||
|
@@ -381,4 +381,50 @@ TEST_F(PerfPkt4Test, UnpackTransactionId) {
|
|||||||
EXPECT_FALSE(pkt2->rawUnpack());
|
EXPECT_FALSE(pkt2->rawUnpack());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(PerfPkt4Test, Writes) {
|
||||||
|
// Initialize intput buffer with 260 elements set to value 1.
|
||||||
|
dhcp::OptionBuffer in_data(260, 1);
|
||||||
|
// Initialize buffer to be used for write: 1,2,3,4,...,9
|
||||||
|
dhcp::OptionBuffer write_buf(10);
|
||||||
|
for (int i = 0; i < write_buf.size(); ++i) {
|
||||||
|
write_buf[i] = i;
|
||||||
|
}
|
||||||
|
// Create packet from the input buffer.
|
||||||
|
const size_t transid_offset = 4;
|
||||||
|
boost::scoped_ptr<PerfPkt4> pkt1(new PerfPkt4(&in_data[0],
|
||||||
|
in_data.size(),
|
||||||
|
transid_offset));
|
||||||
|
// Write numbers 4,5,6,7 to the packet's input buffer at position 10.
|
||||||
|
pkt1->writeAt(10, write_buf.begin() + 3, write_buf.begin() + 7);
|
||||||
|
// We have to pack data to output buffer here because Pkt4 provides no
|
||||||
|
// way to retrieve input buffer. If we pack data it will go to
|
||||||
|
// output buffer that has getter available.
|
||||||
|
ASSERT_TRUE(pkt1->rawPack());
|
||||||
|
const util::OutputBuffer& out_buf = pkt1->getBuffer();
|
||||||
|
ASSERT_EQ(in_data.size(), out_buf.getLength());
|
||||||
|
// Verify that 4,5,6,7 has been written to the packet's buffer.
|
||||||
|
const char* out_data = static_cast<const char*>(out_buf.getData());
|
||||||
|
EXPECT_TRUE(std::equal(write_buf.begin() + 3, write_buf.begin() + 7,
|
||||||
|
out_data + 10));
|
||||||
|
// Write 1 octet (0x51) at position 10.
|
||||||
|
pkt1->writeValueAt<uint8_t>(10, 0x51);
|
||||||
|
ASSERT_TRUE(pkt1->rawPack());
|
||||||
|
ASSERT_EQ(in_data.size(), pkt1->getBuffer().getLength());
|
||||||
|
EXPECT_EQ(0x51, pkt1->getBuffer()[10]);
|
||||||
|
// Write 2 octets (0x5251) at position 20.
|
||||||
|
pkt1->writeValueAt<uint16_t>(20, 0x5251);
|
||||||
|
ASSERT_TRUE(pkt1->rawPack());
|
||||||
|
ASSERT_EQ(in_data.size(), pkt1->getBuffer().getLength());
|
||||||
|
EXPECT_EQ(0x52, pkt1->getBuffer()[20]);
|
||||||
|
EXPECT_EQ(0x51, pkt1->getBuffer()[21]);
|
||||||
|
// Write 4 octets (0x54535251) at position 30.
|
||||||
|
pkt1->writeValueAt<uint32_t>(30, 0x54535251);
|
||||||
|
ASSERT_TRUE(pkt1->rawPack());
|
||||||
|
ASSERT_EQ(in_data.size(), pkt1->getBuffer().getLength());
|
||||||
|
EXPECT_EQ(0x54, pkt1->getBuffer()[30]);
|
||||||
|
EXPECT_EQ(0x53, pkt1->getBuffer()[31]);
|
||||||
|
EXPECT_EQ(0x52, pkt1->getBuffer()[32]);
|
||||||
|
EXPECT_EQ(0x51, pkt1->getBuffer()[33]);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -387,13 +387,13 @@ TEST_F(StatsMgrTest, CustomCounters) {
|
|||||||
// Increment one of the counters 10 times.
|
// Increment one of the counters 10 times.
|
||||||
const uint64_t tooshort_num = 10;
|
const uint64_t tooshort_num = 10;
|
||||||
for (uint64_t i = 0; i < tooshort_num; ++i) {
|
for (uint64_t i = 0; i < tooshort_num; ++i) {
|
||||||
stats_mgr->IncrementCounter(too_short_key);
|
stats_mgr->incrementCounter(too_short_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment another counter by 5 times.
|
// Increment another counter by 5 times.
|
||||||
const uint64_t toolate_num = 5;
|
const uint64_t toolate_num = 5;
|
||||||
for (uint64_t i = 0; i < toolate_num; ++i) {
|
for (uint64_t i = 0; i < toolate_num; ++i) {
|
||||||
stats_mgr->IncrementCounter(too_late_key);
|
stats_mgr->incrementCounter(too_late_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check counter's current value and name.
|
// Check counter's current value and name.
|
||||||
|
1011
tests/tools/perfdhcp/tests/test_control_unittest.cc
Normal file
1011
tests/tools/perfdhcp/tests/test_control_unittest.cc
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user