1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018 |
- // 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 <stdio.h>
- #include <stdlib.h>
- #include <stdint.h>
- #include <unistd.h>
- #include <boost/date_time/posix_time/posix_time.hpp>
- #include <exceptions/exceptions.h>
- #include <asiolink/io_address.h>
- #include <dhcp/libdhcp++.h>
- #include <dhcp/iface_mgr.h>
- #include <dhcp/dhcp4.h>
- #include "test_control.h"
- #include "command_options.h"
- using namespace std;
- using namespace boost;
- using namespace boost::posix_time;
- using namespace isc;
- using namespace isc::dhcp;
- using namespace isc::asiolink;
- namespace isc {
- namespace perfdhcp {
- TestControl::TestControlSocket::TestControlSocket(int socket) :
- socket_(socket),
- addr_("127.0.0.1") {
- initSocketData();
- }
- TestControl::TestControlSocket::~TestControlSocket() {
- IfaceMgr::instance().closeSockets();
- }
- void
- TestControl::TestControlSocket::initSocketData() {
- const IfaceMgr::IfaceCollection& ifaces =
- IfaceMgr::instance().getIfaces();
- for (IfaceMgr::IfaceCollection::const_iterator it = ifaces.begin();
- it != ifaces.end();
- ++it) {
- const IfaceMgr::SocketCollection& socket_collection =
- it->getSockets();
- for (IfaceMgr::SocketCollection::const_iterator s =
- socket_collection.begin();
- s != socket_collection.end();
- ++s) {
- if (s->sockfd_ == socket_) {
- iface_ = it->getName();
- ifindex_ = it->getIndex();
- addr_ = s->addr_;
- return;
- }
- }
- }
- isc_throw(BadValue, "interface for for specified socket "
- "descriptor not found");
- }
- TestControl&
- TestControl::instance() {
- static TestControl test_control;
- return (test_control);
- }
- TestControl::TestControl() {
- reset();
- }
- bool
- TestControl::checkExitConditions() const {
- CommandOptions& options = CommandOptions::instance();
- // Check if test period passed..
- if (options.getPeriod() != 0) {
- if (options.getIpVersion() == 4) {
- time_period period(stats_mgr4_->getTestPeriod());
- if (period.length().total_seconds() >= options.getPeriod()) {
- return true;
- }
- } else if (options.getIpVersion() == 6) {
- time_period period = stats_mgr6_->getTestPeriod();
- if (period.length().total_seconds() >= options.getPeriod()) {
- return true;
- }
- }
- }
- // Check if we reached maximum number of DISCOVER/SOLICIT sent.
- if (options.getNumRequests().size() > 0) {
- if (options.getIpVersion() == 4) {
- if (getSentPacketsNum(StatsMgr4::XCHG_DO) >=
- options.getNumRequests()[0]) {
- return(true);
- }
- } else if (options.getIpVersion() == 6) {
- if (stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA) >=
- options.getNumRequests()[0]) {
- return(true);
- }
- }
- }
- // Check if we reached maximum number REQUEST packets.
- if (options.getNumRequests().size() == 2) {
- if (options.getIpVersion() == 4) {
- if (stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) >=
- options.getNumRequests()[1]) {
- return(true);
- }
- } else if (options.getIpVersion() == 6) {
- if (stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR) >=
- options.getNumRequests()[1]) {
- return(true);
- }
- }
- }
- // Check if we reached maximum number of drops of OFFER/ADVERTISE packets.
- if (options.getMaxDrop().size() > 0) {
- if (options.getIpVersion() == 4) {
- if (stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_DO) >=
- options.getMaxDrop()[0]) {
- return(true);
- }
- } else if (options.getIpVersion() == 6) {
- if (stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_SA) >=
- options.getMaxDrop()[0]) {
- return(true);
- }
- }
- }
- // Check if we reached maximum number of drops of ACK/REPLY packets.
- if (options.getMaxDrop().size() == 2) {
- if (options.getIpVersion() == 4) {
- if (stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) >=
- options.getMaxDrop()[1]) {
- return(true);
- }
- } else if (options.getIpVersion() == 6) {
- if (stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_RR) >=
- options.getMaxDrop()[1]) {
- return(true);
- }
- }
- }
- // Check if we reached maximum drops percentage of OFFER/ADVERTISE packets.
- if (options.getMaxDropPercentage().size() > 0) {
- if (options.getIpVersion() == 4) {
- if ((stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_DO) > 10) &&
- ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_DO) /
- stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_DO)) >=
- options.getMaxDropPercentage()[0])) {
- return(true);
- }
- } else if (options.getIpVersion() == 6) {
- if ((stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA) > 10) &&
- ((100. * stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_SA) /
- stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_SA)) >=
- options.getMaxDropPercentage()[0])) {
- return(true);
- }
- }
- }
- // Check if we reached maximum drops percentage of ACK/REPLY packets.
- if (options.getMaxDropPercentage().size() == 2) {
- if (options.getIpVersion() == 4) {
- if ((stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA) > 10) &&
- ((100. * stats_mgr4_->getDroppedPacketsNum(StatsMgr4::XCHG_RA) /
- stats_mgr4_->getSentPacketsNum(StatsMgr4::XCHG_RA)) >=
- options.getMaxDropPercentage()[1])) {
- return(true);
- }
- } else if (options.getIpVersion() == 6) {
- if ((stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR) > 10) &&
- ((100. * stats_mgr6_->getDroppedPacketsNum(StatsMgr6::XCHG_RR) /
- stats_mgr6_->getSentPacketsNum(StatsMgr6::XCHG_RR)) >=
- options.getMaxDropPercentage()[1])) {
- return(true);
- }
- }
- }
- return(false);
- }
- OptionPtr
- TestControl::factoryElapsedTime6(Option::Universe, uint16_t,
- const OptionBuffer& buf) {
- if (buf.size() == 2) {
- return OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME, buf));
- } else if (buf.size() == 0) {
- return OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME,
- OptionBuffer(2, 0)));
- }
- isc_throw(isc::BadValue,
- "elapsed time option buffer size has to be 0 or 2");
- }
- OptionPtr
- TestControl::factoryGeneric(Option::Universe u, uint16_t type,
- const OptionBuffer& buf) {
- OptionPtr opt(new Option(u, type, buf));
- return opt;
- }
- OptionPtr
- TestControl::factoryIana6(Option::Universe, uint16_t,
- const OptionBuffer& buf) {
- const uint8_t buf_array[] = {
- 0, 0, 0, 1, // IAID = 1
- 0, 0, 3600 >> 8, 3600 && 0xff, // T1 = 3600
- 0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400
- };
- OptionBuffer buf_ia_na(buf_array, buf_array + sizeof(buf_array));
- for (int i = 0; i < buf.size(); ++i) {
- buf_ia_na.push_back(buf[i]);
- }
- return OptionPtr(new Option(Option::V6, D6O_IA_NA, buf_ia_na));
- }
- OptionPtr
- TestControl::factoryRapidCommit6(Option::Universe, uint16_t,
- const OptionBuffer&) {
- return OptionPtr(new Option(Option::V6, D6O_RAPID_COMMIT, OptionBuffer()));
- }
- OptionPtr
- TestControl::factoryOptionRequestOption6(Option::Universe,
- uint16_t,
- const OptionBuffer&) {
- const uint8_t buf_array[] = {
- D6O_NAME_SERVERS,
- D6O_DOMAIN_SEARCH
- };
- OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array));
- return OptionPtr(new Option(Option::V6, D6O_ORO, buf_with_options));
- }
- OptionPtr
- TestControl::factoryRequestList4(Option::Universe u,
- uint16_t type,
- const OptionBuffer& buf) {
- const uint8_t buf_array[] = {
- DHO_SUBNET_MASK,
- DHO_BROADCAST_ADDRESS,
- DHO_TIME_OFFSET,
- DHO_ROUTERS,
- DHO_DOMAIN_NAME,
- DHO_DOMAIN_NAME_SERVERS,
- DHO_HOST_NAME
- };
- OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array));
- OptionPtr opt(new Option(u, type, buf));
- opt->setData(buf_with_options.begin(), buf_with_options.end());
- return opt;
- }
- std::vector<uint8_t>
- TestControl::generateMacAddress() const {
- CommandOptions& options = CommandOptions::instance();
- uint32_t clients_num = options.getClientsNum();
- if ((clients_num == 0) || (clients_num == 1)) {
- return options.getMacPrefix();
- }
- // Get the base MAC address. We are going to randomize part of it.
- std::vector<uint8_t> mac_addr(options.getMacPrefix());
- if (mac_addr.size() != HW_ETHER_LEN) {
- isc_throw(BadValue, "invalid MAC address prefix specified");
- }
- uint32_t r = random();
- // The random number must be in the range 0..clients_num. This
- // will guarantee that every client has exactly one random MAC
- // address assigned.
- r %= clients_num;
- // Randomize MAC address octets.
- for (std::vector<uint8_t>::iterator it = mac_addr.end() - 1;
- it >= mac_addr.begin();
- --it) {
- // Add the random value to the current octet.
- (*it) += r;
- if (r < 256) {
- // If we are here it means that there is no sense
- // to randomize the remaining octets of MAC address
- // because the following bytes of random value
- // are zero and it will have no effect.
- break;
- }
- // Randomize the next octet with the following
- // byte of random value.
- r >>= 8;
- }
- return mac_addr;
- }
- std::vector<uint8_t>
- TestControl::generateDuid() const {
- CommandOptions& options = CommandOptions::instance();
- uint32_t clients_num = options.getClientsNum();
- if ((clients_num == 0) || (clients_num == 1)) {
- return options.getDuidPrefix();
- }
- // Get the base DUID. We are going to randomize part of it.
- std::vector<uint8_t> duid(options.getDuidPrefix());
- std::vector<uint8_t> mac_addr(generateMacAddress());
- duid.resize(duid.size() - mac_addr.size());
- for (int i = 0; i < mac_addr.size(); ++i) {
- duid.push_back(mac_addr[i]);
- }
- return duid;
- }
- uint64_t
- TestControl::getNextExchangesNum() const {
- CommandOptions& options = CommandOptions::instance();
- // Reset number of exchanges.
- uint64_t due_exchanges = 0;
- // Get current time.
- ptime now(microsec_clock::universal_time());
- // The due time indicates when we should start sending next chunk
- // of packets. If it is already due time, we should calculate
- // how many packets to send.
- if (now >= send_due_) {
- // If rate is specified from the command line we have to
- // synchornize with it.
- if (options.getRate() != 0) {
- time_period period(send_due_, now);
- time_duration duration = period.length();
- // due_factor indicates the number of seconds that
- // sending next chunk of packets will take.
- double due_factor = duration.fractional_seconds() /
- time_duration::ticks_per_second();
- due_factor += duration.total_seconds();
- // Multiplying due_factor by expected rate gives the number
- // of exchanges to be initiated.
- due_exchanges = static_cast<uint64_t>(due_factor * options.getRate());
- // We want to make sure that at least one packet goes out.
- due_exchanges += 1;
- // We should not exceed aggressivity as it could have been
- // restricted from command line.
- if (due_exchanges > options.getAggressivity()) {
- due_exchanges = options.getAggressivity();
- }
- } else {
- // Rate is not specified so we rely on aggressivity
- // which is the number of packets to be sent in
- // one chunk.
- due_exchanges = options.getAggressivity();
- }
- return (due_exchanges);
- }
- return (0);
- }
- uint64_t
- TestControl::getRcvdPacketsNum(const ExchangeType xchg_type) const {
- uint8_t ip_version = CommandOptions::instance().getIpVersion();
- if (ip_version == 4) {
- return(stats_mgr4_->getRcvdPacketsNum(xchg_type));
- }
- return(stats_mgr6_->
- getRcvdPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
- }
- uint64_t
- TestControl::getSentPacketsNum(const ExchangeType xchg_type) const {
- uint8_t ip_version = CommandOptions::instance().getIpVersion();
- if (ip_version == 4) {
- return(stats_mgr4_->getSentPacketsNum(xchg_type));
- }
- return(stats_mgr6_->
- getSentPacketsNum(static_cast<StatsMgr6::ExchangeType>(xchg_type)));
- }
- void
- TestControl::initPacketTemplates() {
- CommandOptions& options = CommandOptions::instance();
- std::vector<std::string> template_files = options.getTemplateFiles();
- for (std::vector<std::string>::const_iterator it = template_files.begin();
- it != template_files.end();
- ++it) {
- std::cout << "Open file " << *it << std::endl;
- }
- }
- void
- TestControl::initializeStatsMgr() {
- CommandOptions& options = CommandOptions::instance();
- if (options.getIpVersion() == 4) {
- stats_mgr4_.reset();
- stats_mgr4_ = StatsMgr4Ptr(new StatsMgr4());
- stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_DO);
- if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
- stats_mgr4_->addExchangeStats(StatsMgr4::XCHG_RA);
- }
- } else if (options.getIpVersion() == 6) {
- stats_mgr6_.reset();
- stats_mgr6_ = StatsMgr6Ptr(new StatsMgr6());
- stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_SA);
- if (options.getExchangeMode() == CommandOptions::DORA_SARR) {
- stats_mgr6_->addExchangeStats(StatsMgr6::XCHG_RR);
- }
- }
- }
- int
- TestControl::openSocket() const {
- CommandOptions& options = CommandOptions::instance();
- std::string localname = options.getLocalName();
- std::string servername = options.getServerName();
- uint16_t port = options.getLocalPort();
- uint8_t family = AF_INET;
- int sock = 0;
- IOAddress remoteaddr(servername);
- if (port == 0) {
- if (options.getIpVersion() == 6) {
- port = DHCP6_CLIENT_PORT;
- } else if (options.getIpVersion() == 4) {
- port = 67; // TODO: find out why port 68 is wrong here.
- }
- }
- if (options.getIpVersion() == 6) {
- family = AF_INET6;
- }
- // Local name is specified along with '-l' option.
- // It may point to interface name or local address.
- if (!localname.empty()) {
- // CommandOptions should be already aware wether local name
- // is interface name or address because it uses IfaceMgr to
- // scan interfaces and get's their names.
- if (options.isInterface()) {
- sock = IfaceMgr::instance().openSocketFromIface(localname,
- port,
- family);
- } else {
- IOAddress localaddr(localname);
- sock = IfaceMgr::instance().openSocketFromAddress(localaddr,
- port);
- }
- } else if (!servername.empty()) {
- // If only server name is given we will need to try to resolve
- // the local address to bind socket to based on remote address.
- sock = IfaceMgr::instance().openSocketFromRemoteAddress(remoteaddr,
- port);
- }
- if (sock <= 0) {
- isc_throw(BadValue, "unable to open socket to communicate with "
- "DHCP server");
- }
- // IfaceMgr does not set broadcast option on the socket. We rely
- // on CommandOptions object to find out if socket has to have
- // broadcast enabled.
- if ((options.getIpVersion() == 4) && options.isBroadcast()) {
- int broadcast_enable = 1;
- int ret = setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
- &broadcast_enable, sizeof(broadcast_enable));
- if (ret < 0) {
- isc_throw(InvalidOperation,
- "unable to set broadcast option on the socket");
- }
- } else if (options.getIpVersion() == 6) {
- // If remote address is multicast we need to enable it on
- // the socket that has been created.
- asio::ip::address_v6 remote_v6 = remoteaddr.getAddress().to_v6();
- if (remote_v6.is_multicast()) {
- int hops = 1;
- int ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
- &hops, sizeof(hops));
- // If user specified interface name with '-l' the
- // IPV6_MULTICAST_IF has to be set.
- if ((ret >= 0) && options.isInterface()) {
- IfaceMgr::Iface* iface =
- IfaceMgr::instance().getIface(options.getLocalName());
- if (iface == NULL) {
- isc_throw(Unexpected, "unknown interface "
- << options.getLocalName());
- }
- int idx = iface->getIndex();
- ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_IF,
- &idx, sizeof(idx));
- }
- if (ret < 0) {
- isc_throw(InvalidOperation,
- "unable to enable multicast on socket " << sock
- << ". errno = " << errno);
- }
- }
- }
- return(sock);
- }
- void
- TestControl::printRate() const {
- double rate = 0;
- CommandOptions& options = CommandOptions::instance();
- if (options.getIpVersion() == 4) {
- double duration =
- stats_mgr4_->getTestPeriod().length().total_nanoseconds() / 1e9;
- rate = stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_DO) / duration;
- } else if (options.getIpVersion() == 6) {
- double duration =
- stats_mgr6_->getTestPeriod().length().total_nanoseconds() / 1e9;
- rate = stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_SA) / duration;
- }
- std::cout << "***Rate statistics***" << std::endl;
- if (options.getRate() > 0) {
- std::cout << "Rate: " << rate << ", expected rate: "
- << options.getRate() << std::endl << std::endl;
- } else {
- std::cout << "Rate: " << rate << std::endl << std::endl;
- }
- }
- void
- TestControl::printIntermediateStats() {
- CommandOptions& options = CommandOptions::instance();
- int delay = options.getReportDelay();
- ptime now = microsec_clock::universal_time();
- time_period time_since_report(last_report_, now);
- if (time_since_report.length().total_seconds() >= delay) {
- if (options.getIpVersion() == 4) {
- stats_mgr4_->printIntermediateStats();
- } else if (options.getIpVersion() == 6) {
- stats_mgr6_->printIntermediateStats();
- }
- last_report_ = now;
- }
- }
- void
- TestControl::printStats() const {
- printRate();
- CommandOptions& options = CommandOptions::instance();
- if (options.getIpVersion() == 4) {
- if (!stats_mgr4_) {
- isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
- "hasn't been initialized");
- }
- stats_mgr4_->printStats();
- } else if (options.getIpVersion() == 6) {
- if (!stats_mgr6_) {
- isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
- "hasn't been initialized");
- }
- stats_mgr6_->printStats();
- }
- }
- void
- TestControl::receivePacket4(const TestControlSocket& socket,
- const Pkt4Ptr& pkt4) {
- if (pkt4->getType() == DHCPOFFER) {
- Pkt4Ptr discover_pkt4(stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_DO,
- pkt4));
- CommandOptions::ExchangeMode xchg_mode =
- CommandOptions::instance().getExchangeMode();
- if ((xchg_mode == CommandOptions::DORA_SARR) && discover_pkt4) {
- sendRequest4(socket, pkt4);
- }
- } else if (pkt4->getType() == DHCPACK) {
- stats_mgr4_->passRcvdPacket(StatsMgr4::XCHG_RA, pkt4);
- }
- }
- void
- TestControl::receivePacket6(const TestControlSocket& socket,
- const Pkt6Ptr& pkt6) {
- uint8_t packet_type = pkt6->getType();
- if (packet_type == DHCPV6_ADVERTISE) {
- Pkt6Ptr solicit_pkt6(stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_SA,
- pkt6));
- CommandOptions::ExchangeMode xchg_mode =
- CommandOptions::instance().getExchangeMode();
- if ((xchg_mode == CommandOptions::DORA_SARR) && solicit_pkt6) {
- sendRequest6(socket, solicit_pkt6, pkt6);
- }
- } else if (packet_type == DHCPV6_REPLY) {
- stats_mgr6_->passRcvdPacket(StatsMgr6::XCHG_RR, pkt6);
- }
- }
- void
- TestControl::receivePackets(const TestControlSocket& socket) {
- int timeout = 0;
- bool receiving = true;
- while (receiving) {
- if (CommandOptions::instance().getIpVersion() == 4) {
- Pkt4Ptr pkt4 = IfaceMgr::instance().receive4(timeout);
- if (!pkt4) {
- receiving = false;
- } else {
- pkt4->unpack();
- receivePacket4(socket, pkt4);
- }
- } else if (CommandOptions::instance().getIpVersion() == 6) {
- Pkt6Ptr pkt6 = IfaceMgr::instance().receive6(timeout);
- if (!pkt6) {
- receiving = false;
- } else {
- if (pkt6->unpack()) {
- receivePacket6(socket, pkt6);
- }
- }
- }
- }
- }
- void
- TestControl::registerOptionFactories4() const {
- static bool factories_registered = false;
- if (!factories_registered) {
- // DHCP_MESSAGE_TYPE option factory.
- LibDHCP::OptionFactoryRegister(Option::V4,
- DHO_DHCP_MESSAGE_TYPE,
- &TestControl::factoryGeneric);
- LibDHCP::OptionFactoryRegister(Option::V4,
- DHO_DHCP_SERVER_IDENTIFIER,
- &TestControl::factoryGeneric);
- // DHCP_PARAMETER_REQUEST_LIST option factory.
- LibDHCP::OptionFactoryRegister(Option::V4,
- DHO_DHCP_PARAMETER_REQUEST_LIST,
- &TestControl::factoryRequestList4);
- }
- factories_registered = true;
- }
- void
- TestControl::registerOptionFactories6() const {
- static bool factories_registered = false;
- if (!factories_registered) {
- LibDHCP::OptionFactoryRegister(Option::V6,
- D6O_ELAPSED_TIME,
- &TestControl::factoryElapsedTime6);
- LibDHCP::OptionFactoryRegister(Option::V6,
- D6O_RAPID_COMMIT,
- &TestControl::factoryRapidCommit6);
- LibDHCP::OptionFactoryRegister(Option::V6,
- D6O_ORO,
- &TestControl::factoryOptionRequestOption6);
- LibDHCP::OptionFactoryRegister(Option::V6,
- D6O_CLIENTID,
- &TestControl::factoryGeneric);
- LibDHCP::OptionFactoryRegister(Option::V6,
- D6O_SERVERID,
- &TestControl::factoryGeneric);
- LibDHCP::OptionFactoryRegister(Option::V6,
- D6O_IA_NA,
- &TestControl::factoryIana6);
- }
- factories_registered = true;
- }
- void
- TestControl::registerOptionFactories() const {
- CommandOptions& options = CommandOptions::instance();
- switch(options.getIpVersion()) {
- case 4:
- registerOptionFactories4();
- break;
- case 6:
- registerOptionFactories6();
- break;
- default:
- isc_throw(InvalidOperation, "command line options have to be parsed "
- "before DHCP option factories can be registered");
- }
- }
- void
- TestControl::reset() {
- send_due_ = microsec_clock::universal_time();
- last_sent_ = send_due_;
- last_report_ = send_due_;
- transid_gen_.reset();
- transid_gen_ = TransidGeneratorPtr(new TransidGenerator());
- first_packet_serverid_.clear();
- }
- void
- TestControl::run() {
- sent_packets_0_ = 0;
- sent_packets_1_ = 0;
- // Reset singleton state before test starts.
- reset();
- CommandOptions& options = CommandOptions::instance();
- // Ip version is not set ONLY in case the command options
- // were not parsed. This surely means that parse() function
- // was not called prior to starting the test. This is fatal
- // error.
- if (options.getIpVersion() == 0) {
- isc_throw(InvalidOperation,
- "command options must be parsed before running a test");
- }
- registerOptionFactories();
- TestControlSocket socket(openSocket());
- // Initialize packet templates.
- initPacketTemplates();
- // Initialize randomization seed.
- if (options.isSeeded()) {
- srandom(options.getSeed());
- }
- // Preload server with number of packets.
- const bool do_preload = true;
- for (int i = 0; i < options.getPreload(); ++i) {
- if (options.getIpVersion() == 4) {
- sendDiscover4(socket, do_preload);
- } else if (options.getIpVersion() == 6) {
- sendSolicit6(socket, do_preload);
- }
- }
- initializeStatsMgr();
- uint64_t packets_sent = 0;
- for (;;) {
- updateSendDue();
- if (checkExitConditions()) {
- break;
- }
- uint64_t packets_due = getNextExchangesNum();
- receivePackets(socket);
- for (uint64_t i = packets_due; i > 0; --i) {
- if (options.getIpVersion() == 4) {
- sendDiscover4(socket);
- } else {
- sendSolicit6(socket);
- }
- ++packets_sent;
- }
- if (options.getReportDelay() > 0) {
- printIntermediateStats();
- }
- }
- printStats();
- }
- void
- TestControl::sendDiscover4(const TestControlSocket& socket,
- const bool preload /*= false*/) {
- ++sent_packets_0_;
- last_sent_ = microsec_clock::universal_time();
- // Generate the MAC address to be passed in the packet.
- std::vector<uint8_t> mac_address = generateMacAddress();
- // Generate trasnaction id to be set for the new exchange.
- const uint32_t transid = generateTransid();
- Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, transid));
- if (!pkt4) {
- isc_throw(Unexpected, "failed to create DISCOVER packet");
- }
- // Set options: DHCP_MESSAGE_TYPE and DHCP_PARAMETER_REQUEST_LIST
- OptionBuffer buf_msg_type;
- buf_msg_type.push_back(DHCPDISCOVER);
- pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE,
- buf_msg_type));
- pkt4->addOption(Option::factory(Option::V4,
- DHO_DHCP_PARAMETER_REQUEST_LIST));
- // Set client's and server's ports as well as server's address,
- // and local (relay) address.
- setDefaults4(socket, pkt4);
- pkt4->pack();
- IfaceMgr::instance().send(pkt4);
- if (!preload) {
- if (!stats_mgr4_) {
- isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
- "hasn't been initialized");
- }
- stats_mgr4_->passSentPacket(StatsMgr4::XCHG_DO, pkt4);
- }
- }
- void
- TestControl::sendRequest4(const TestControlSocket& socket,
- const dhcp::Pkt4Ptr& offer_pkt4) {
- const uint32_t transid = generateTransid();
- Pkt4Ptr pkt4(new Pkt4(DHCPREQUEST, transid));
- OptionBuffer buf_msg_type;
- buf_msg_type.push_back(DHCPREQUEST);
- OptionPtr opt_msg_type = Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE,
- buf_msg_type);
- pkt4->addOption(opt_msg_type);
- if (first_packet_serverid_.size() > 0) {
- pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_SERVER_IDENTIFIER,
- first_packet_serverid_));
- } else {
- OptionPtr opt_serverid =
- offer_pkt4->getOption(DHO_DHCP_SERVER_IDENTIFIER);
- if (!opt_serverid) {
- isc_throw(BadValue, "there is no SERVER_IDENTIFIER option "
- << "in OFFER message");
- }
- if (CommandOptions::instance().isUseFirst() &&
- stats_mgr4_->getRcvdPacketsNum(StatsMgr4::XCHG_DO) == 1) {
- first_packet_serverid_ = opt_serverid->getData();
- }
- pkt4->addOption(opt_serverid);
- }
- /// Set client address.
- asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr();
- if (!yiaddr.getAddress().is_v4()) {
- isc_throw(BadValue, "the YIADDR returned in OFFER packet is not "
- " IPv4 address");
- }
- OptionPtr opt_requested_address =
- OptionPtr(new Option(Option::V4, DHO_DHCP_REQUESTED_ADDRESS,
- OptionBuffer()));
- opt_requested_address->setUint32(yiaddr);
- pkt4->addOption(opt_requested_address);
- OptionPtr opt_parameter_list =
- Option::factory(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST);
- pkt4->addOption(opt_parameter_list);
- // Set client's and server's ports as well as server's address,
- // and local (relay) address.
- setDefaults4(socket, pkt4);
- pkt4->pack();
- IfaceMgr::instance().send(pkt4);
- if (!stats_mgr4_) {
- isc_throw(InvalidOperation, "Statistics Manager for DHCPv4 "
- "hasn't been initialized");
- }
- stats_mgr4_->passSentPacket(StatsMgr4::XCHG_RA, pkt4);
- }
- void
- TestControl::sendRequest6(const TestControlSocket& socket,
- const Pkt6Ptr& solicit_pkt6,
- const Pkt6Ptr& advertise_pkt6) {
- const uint32_t transid = generateTransid();
- Pkt6Ptr pkt6(new Pkt6(DHCPV6_REQUEST, transid));
- // Calculate elapsed time
- ptime solicit_time = solicit_pkt6->getTimestamp();
- ptime advertise_time = advertise_pkt6->getTimestamp();
- if (solicit_time.is_not_a_date_time()) {
- isc_throw(Unexpected, "timestamp was not set for SOLICIT packet");
- }
- if (advertise_time.is_not_a_date_time()) {
- isc_throw(Unexpected, "timestamp was not set for ADVERTISE packet");
- }
- time_period period(solicit_time, advertise_time);
- if (period.is_null()) {
- pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME));
- } else {
- OptionBuffer buf();
- const uint32_t elapsed_time = period.length().total_seconds();
- OptionPtr opt_elapsed_time =
- Option::factory(Option::V6, D6O_ELAPSED_TIME);
- opt_elapsed_time->setUint16(static_cast<uint16_t>(elapsed_time));
- pkt6->addOption(opt_elapsed_time);
- }
- OptionPtr opt_clientid = advertise_pkt6->getOption(D6O_CLIENTID);
- if (!opt_clientid) {
- isc_throw(Unexpected, "client id not found in received packet");
- }
- pkt6->addOption(opt_clientid);
- if (first_packet_serverid_.size() > 0) {
- pkt6->addOption(Option::factory(Option::V6, D6O_SERVERID,
- first_packet_serverid_));
- } else {
- OptionPtr opt_serverid = advertise_pkt6->getOption(D6O_SERVERID);
- if (!opt_serverid) {
- isc_throw(Unexpected, "server id not found in received packet");
- }
- if (CommandOptions::instance().isUseFirst() &&
- stats_mgr6_->getRcvdPacketsNum(StatsMgr6::XCHG_SA) == 1) {
- first_packet_serverid_ = opt_serverid->getData();
- }
- pkt6->addOption(opt_serverid);
- }
- OptionPtr opt_ia_na = advertise_pkt6->getOption(D6O_IA_NA);
- if (!opt_ia_na) {
- isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received "
- "packet");
- }
- pkt6->addOption(opt_ia_na);
- setDefaults6(socket, pkt6);
- pkt6->pack();
- IfaceMgr::instance().send(pkt6);
- if (!stats_mgr6_) {
- isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
- "hasn't been initialized");
- }
- stats_mgr6_->passSentPacket(StatsMgr6::XCHG_RR, pkt6);
- }
- void
- TestControl::sendSolicit6(const TestControlSocket& socket,
- const bool preload /*= false*/) {
- ++sent_packets_0_;
- last_sent_ = microsec_clock::universal_time();
- // Generate the MAC address to be passed in the packet.
- std::vector<uint8_t> mac_address = generateMacAddress();
- // Generate DUID to be passed to the packet
- std::vector<uint8_t> duid = generateDuid();
- // Generate trasnaction id to be set for the new exchange.
- const uint32_t transid = generateTransid();
- Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
- if (!pkt6) {
- isc_throw(Unexpected, "failed to create SOLICIT packet");
- }
- pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME));
- if (CommandOptions::instance().isRapidCommit()) {
- pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT));
- }
- pkt6->addOption(Option::factory(Option::V6, D6O_CLIENTID, duid));
- pkt6->addOption(Option::factory(Option::V6, D6O_ORO));
- pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA));
- setDefaults6(socket, pkt6);
- pkt6->pack();
- IfaceMgr::instance().send(pkt6);
- if (!preload) {
- if (!stats_mgr6_) {
- isc_throw(InvalidOperation, "Statistics Manager for DHCPv6 "
- "hasn't been initialized");
- }
- stats_mgr6_->passSentPacket(StatsMgr6::XCHG_SA, pkt6);
- }
- }
- void
- TestControl::setDefaults4(const TestControlSocket& socket,
- const Pkt4Ptr& pkt) {
- CommandOptions& options = CommandOptions::instance();
- // Interface name.
- pkt->setIface(socket.getIface());
- // Interface index.
- pkt->setIndex(socket.getIfIndex());
- // Local client's port (68)
- pkt->setLocalPort(DHCP4_CLIENT_PORT);
- // Server's port (67)
- pkt->setRemotePort(DHCP4_SERVER_PORT);
- // The remote server's name or IP.
- pkt->setRemoteAddr(IOAddress(options.getServerName()));
- // Set local addresss.
- pkt->setLocalAddr(IOAddress(socket.getAddress()));
- // Set relay (GIADDR) address to local address.
- pkt->setGiaddr(IOAddress(socket.getAddress()));
- std::vector<uint8_t> mac = generateMacAddress();
- // Set hardware address
- pkt->setHWAddr(HTYPE_ETHER, mac.size(), mac);
- // Pretend that we have one relay (which is us).
- pkt->setHops(1);
- }
- void
- TestControl::setDefaults6(const TestControlSocket& socket,
- const Pkt6Ptr& pkt) {
- CommandOptions& options = CommandOptions::instance();
- // Interface name.
- pkt->setIface(socket.getIface());
- // Interface index.
- pkt->setIndex(socket.getIfIndex());
- // Local client's port (547)
- pkt->setLocalPort(DHCP6_CLIENT_PORT);
- // Server's port (548)
- pkt->setRemotePort(DHCP6_SERVER_PORT);
- // Set local address.
- pkt->setLocalAddr(socket.getAddress());
- // The remote server's name or IP.
- pkt->setRemoteAddr(IOAddress(options.getServerName()));
- }
- void
- TestControl::updateSendDue() {
- // If default constructor was called, this should not happen but
- // if somebody has changed default constructor it is better to
- // keep this check.
- if (last_sent_.is_not_a_date_time()) {
- isc_throw(Unexpected, "time of last sent packet not initialized");
- }
- // Get the expected exchange rate.
- CommandOptions& options = CommandOptions::instance();
- int rate = options.getRate();
- // If rate was not specified we will wait just one clock tick to
- // send next packet. This simulates best effort conditions.
- long duration = 1;
- if (rate != 0) {
- // We use number of ticks instead of nanoseconds because
- // nanosecond resolution may not be available on some
- // machines. Number of ticks guarantees the highest possible
- // timer resolution.
- duration = time_duration::ticks_per_second() / rate;
- }
- // Calculate due time to initate next chunk of exchanges.
- send_due_ = last_sent_ + time_duration(0, 0, 0, duration);
- }
- } // namespace perfdhcp
- } // namespace isc
|