Kea  1.9.9-git
test_control.cc
Go to the documentation of this file.
1 // Copyright (C) 2012-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
10 #include <perfdhcp/receiver.h>
12 #include <perfdhcp/perf_pkt4.h>
13 #include <perfdhcp/perf_pkt6.h>
14 
15 #include <exceptions/exceptions.h>
16 #include <dhcp/libdhcp++.h>
17 #include <dhcp/iface_mgr.h>
18 #include <dhcp/dhcp4.h>
19 #include <dhcp/option6_ia.h>
20 #include <dhcp/option6_iaaddr.h>
21 #include <dhcp/option6_iaprefix.h>
22 #include <dhcp/option_int.h>
24 
25 #include <boost/date_time/posix_time/posix_time.hpp>
26 #include <algorithm>
27 #include <fstream>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdint.h>
31 #include <unistd.h>
32 #include <signal.h>
33 #include <sstream>
34 #include <sys/wait.h>
35 
36 using namespace std;
37 using namespace boost::posix_time;
38 using namespace isc;
39 using namespace isc::dhcp;
40 using namespace isc::asiolink;
41 
42 namespace isc {
43 namespace perfdhcp {
44 
45 bool TestControl::interrupted_ = false;
46 
47 bool
48 TestControl::waitToExit() {
49  uint32_t const wait_time = options_.getExitWaitTime();
50 
51  // If we care and not all packets are in yet
52  if (wait_time && !haveAllPacketsBeenReceived()) {
53  const ptime now = microsec_clock::universal_time();
54 
55  // Init the end time if it hasn't started yet
56  if (exit_time_.is_not_a_date_time()) {
57  exit_time_ = now + time_duration(microseconds(wait_time));
58  }
59 
60  // If we're not at end time yet, return true
61  return (now < exit_time_);
62  }
63 
64  // No need to wait, return false;
65  return (false);
66 }
67 
68 bool
69 TestControl::haveAllPacketsBeenReceived() const {
70  const uint8_t& ipversion = options_.getIpVersion();
71  const std::vector<int>& num_request = options_.getNumRequests();
72  const size_t& num_request_size = num_request.size();
73 
74  if (num_request_size == 0) {
75  return false;
76  }
77 
78  uint32_t responses = 0;
79  uint32_t requests = num_request[0];
80  if (num_request_size >= 2) {
81  requests += num_request[1];
82  }
83 
84  if (ipversion == 4) {
85  responses = stats_mgr_.getRcvdPacketsNum(ExchangeType::DO) +
86  stats_mgr_.getRcvdPacketsNum(ExchangeType::RA);
87  } else {
88  responses = stats_mgr_.getRcvdPacketsNum(ExchangeType::SA) +
89  stats_mgr_.getRcvdPacketsNum(ExchangeType::RR);
90  }
91 
92  return (responses == requests);
93 }
94 
95 void
96 TestControl::cleanCachedPackets() {
97  // When Renews are not sent, Reply packets are not cached so there
98  // is nothing to do.
99  if (options_.getRenewRate() == 0) {
100  return;
101  }
102 
103  static boost::posix_time::ptime last_clean =
104  microsec_clock::universal_time();
105 
106  // Check how much time has passed since last cleanup.
107  time_period time_since_clean(last_clean,
108  microsec_clock::universal_time());
109  // Cleanup every 1 second.
110  if (time_since_clean.length().total_seconds() >= 1) {
111  // Calculate how many cached packets to remove. Actually we could
112  // just leave enough packets to handle Renews for 1 second but
113  // since we want to randomize leases to be renewed so leave 5
114  // times more packets to randomize from.
116  if (reply_storage_.size() > 5 * options_.getRenewRate()) {
117  reply_storage_.clear(reply_storage_.size() -
118  5 * options_.getRenewRate());
119  }
120  // Remember when we performed a cleanup for the last time.
121  // We want to do the next cleanup not earlier than in one second.
122  last_clean = microsec_clock::universal_time();
123  }
124 }
125 
126 void
127 TestControl::copyIaOptions(const Pkt6Ptr& pkt_from, Pkt6Ptr& pkt_to) {
128  if (!pkt_from || !pkt_to) {
129  isc_throw(BadValue, "NULL pointers must not be specified as arguments"
130  " for the copyIaOptions function");
131  }
132  // IA_NA
133  if (options_.getLeaseType()
134  .includes(CommandOptions::LeaseType::ADDRESS)) {
135  OptionPtr option = pkt_from->getOption(D6O_IA_NA);
136  if (!option) {
137  isc_throw(NotFound, "IA_NA option not found in the"
138  " server's response");
139  }
140  pkt_to->addOption(option);
141  }
142  // IA_PD
143  if (options_.getLeaseType()
144  .includes(CommandOptions::LeaseType::PREFIX)) {
145  OptionPtr option = pkt_from->getOption(D6O_IA_PD);
146  if (!option) {
147  isc_throw(NotFound, "IA_PD option not found in the"
148  " server's response");
149  }
150  pkt_to->addOption(option);
151  }
152 }
153 
154 std::string
155 TestControl::byte2Hex(const uint8_t b) {
156  const int b1 = b / 16;
157  const int b0 = b % 16;
158  ostringstream stream;
159  stream << std::hex << b1 << b0 << std::dec;
160  return (stream.str());
161 }
162 
163 Pkt4Ptr
164 TestControl::createRequestFromAck(const dhcp::Pkt4Ptr& ack) {
165  if (!ack) {
166  isc_throw(isc::BadValue, "Unable to create DHCPREQUEST from a"
167  " null DHCPACK message");
168  } else if (ack->getYiaddr().isV4Zero()) {
169  isc_throw(isc::BadValue, "Unable to create DHCPREQUEST from a"
170  " DHCPACK message containing yiaddr of 0");
171  }
172  Pkt4Ptr msg(new Pkt4(DHCPREQUEST, generateTransid()));
173  msg->setCiaddr(ack->getYiaddr());
174  msg->setHWAddr(ack->getHWAddr());
175  msg->addOption(generateClientId(msg->getHWAddr()));
176  return (msg);
177 }
178 
179 Pkt6Ptr
180 TestControl::createMessageFromReply(const uint16_t msg_type,
181  const dhcp::Pkt6Ptr& reply) {
182  // Restrict messages to Release and Renew.
183  if (msg_type != DHCPV6_RENEW && msg_type != DHCPV6_RELEASE) {
184  isc_throw(isc::BadValue, "invalid message type " << msg_type
185  << " to be created from Reply, expected DHCPV6_RENEW or"
186  " DHCPV6_RELEASE");
187  }
188  // Get the string representation of the message - to be used for error
189  // logging purposes.
190  const char* msg_type_str = (msg_type == DHCPV6_RENEW ? "Renew" : "Release");
191  // Reply message must be specified.
192  if (!reply) {
193  isc_throw(isc::BadValue, "Unable to create " << msg_type_str
194  << " message from the Reply message because the instance of"
195  " the Reply message is NULL");
196  }
197 
198  Pkt6Ptr msg(new Pkt6(msg_type, generateTransid()));
199  // Client id.
200  OptionPtr opt_clientid = reply->getOption(D6O_CLIENTID);
201  if (!opt_clientid) {
202  isc_throw(isc::Unexpected, "failed to create " << msg_type_str
203  << " message because client id option has not been found"
204  " in the Reply message");
205  }
206  msg->addOption(opt_clientid);
207  // Server id.
208  OptionPtr opt_serverid = reply->getOption(D6O_SERVERID);
209  if (!opt_serverid) {
210  isc_throw(isc::Unexpected, "failed to create " << msg_type_str
211  << " because server id option has not been found in the"
212  " Reply message");
213  }
214  msg->addOption(opt_serverid);
215  copyIaOptions(reply, msg);
216  return (msg);
217 }
218 
219 OptionPtr
220 TestControl::factoryElapsedTime6(Option::Universe, uint16_t,
221  const OptionBuffer& buf) {
222  if (buf.size() == 2) {
223  return (OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME, buf)));
224  } else if (buf.size() == 0) {
225  return (OptionPtr(new Option(Option::V6, D6O_ELAPSED_TIME,
226  OptionBuffer(2, 0))));
227  }
229  "elapsed time option buffer size has to be 0 or 2");
230 }
231 
232 OptionPtr
233 TestControl::factoryGeneric(Option::Universe u, uint16_t type,
234  const OptionBuffer& buf) {
235  OptionPtr opt(new Option(u, type, buf));
236  return (opt);
237 }
238 
239 OptionPtr
240 TestControl::factoryIana6(Option::Universe, uint16_t,
241  const OptionBuffer& buf) {
243  const uint8_t buf_array[] = {
244  0, 0, 0, 1, // IAID = 1
245  0, 0, 3600 >> 8, 3600 & 0xff, // T1 = 3600
246  0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400
247  };
248  OptionBuffer buf_ia_na(buf_array, buf_array + sizeof(buf_array));
249  for (size_t i = 0; i < buf.size(); ++i) {
250  buf_ia_na.push_back(buf[i]);
251  }
252  return (OptionPtr(new Option(Option::V6, D6O_IA_NA, buf_ia_na)));
253 }
254 
255 OptionPtr
256 TestControl::factoryIapd6(Option::Universe, uint16_t,
257  const OptionBuffer& buf) {
259  static const uint8_t buf_array[] = {
260  0, 0, 0, 1, // IAID = 1
261  0, 0, 3600 >> 8, 3600 & 0xff, // T1 = 3600
262  0, 0, 5400 >> 8, 5400 & 0xff, // T2 = 5400
263  };
264  OptionBuffer buf_ia_pd(buf_array, buf_array + sizeof(buf_array));
265  // Append sub-options to IA_PD.
266  buf_ia_pd.insert(buf_ia_pd.end(), buf.begin(), buf.end());
267  return (OptionPtr(new Option(Option::V6, D6O_IA_PD, buf_ia_pd)));
268 }
269 
270 
271 OptionPtr
272 TestControl::factoryRapidCommit6(Option::Universe, uint16_t,
273  const OptionBuffer&) {
274  return (OptionPtr(new Option(Option::V6, D6O_RAPID_COMMIT, OptionBuffer())));
275 }
276 
277 OptionPtr
278 TestControl::factoryOptionRequestOption6(Option::Universe,
279  uint16_t,
280  const OptionBuffer&) {
281  const uint8_t buf_array[] = {
282  0, D6O_NAME_SERVERS,
284  };
285  OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array));
286  return (OptionPtr(new Option(Option::V6, D6O_ORO, buf_with_options)));
287 }
288 
289 
290 OptionPtr
291 TestControl::factoryRequestList4(Option::Universe u,
292  uint16_t type,
293  const OptionBuffer& buf) {
294  const uint8_t buf_array[] = {
298  DHO_ROUTERS,
302  };
303 
304  OptionBuffer buf_with_options(buf_array, buf_array + sizeof(buf_array));
305  OptionPtr opt(new Option(u, type, buf));
306  opt->setData(buf_with_options.begin(), buf_with_options.end());
307  return (opt);
308 }
309 
310 std::vector<uint8_t>
311 TestControl::generateMacAddress(uint8_t& randomized) {
312  const CommandOptions::MacAddrsVector& macs = options_.getMacsFromFile();
313  // if we are using the -M option return a random one from the list...
314  if (macs.size() > 0) {
315  uint16_t r = number_generator_();
316  if (r >= macs.size()) {
317  r = 0;
318  }
319  return macs[r];
320 
321  } else {
322  // ... otherwise use the standard behavior
323  uint32_t clients_num = options_.getClientsNum();
324  if (clients_num < 2) {
325  return (options_.getMacTemplate());
326  }
327  // Get the base MAC address. We are going to randomize part of it.
328  std::vector<uint8_t> mac_addr(options_.getMacTemplate());
329  if (mac_addr.size() != HW_ETHER_LEN) {
330  isc_throw(BadValue, "invalid MAC address template specified");
331  }
332  uint32_t r = macaddr_gen_->generate();
333  randomized = 0;
334  // Randomize MAC address octets.
335  for (std::vector<uint8_t>::iterator it = mac_addr.end() - 1;
336  it >= mac_addr.begin();
337  --it) {
338  // Add the random value to the current octet.
339  (*it) += r;
340  ++randomized;
341  if (r < 256) {
342  // If we are here it means that there is no sense
343  // to randomize the remaining octets of MAC address
344  // because the following bytes of random value
345  // are zero and it will have no effect.
346  break;
347  }
348  // Randomize the next octet with the following
349  // byte of random value.
350  r >>= 8;
351  }
352  return (mac_addr);
353  }
354 }
355 
356 OptionPtr
357 TestControl::generateClientId(const dhcp::HWAddrPtr& hwaddr) const {
358  std::vector<uint8_t> client_id(1, static_cast<uint8_t>(hwaddr->htype_));
359  client_id.insert(client_id.end(), hwaddr->hwaddr_.begin(),
360  hwaddr->hwaddr_.end());
361  return (OptionPtr(new Option(Option::V4, DHO_DHCP_CLIENT_IDENTIFIER,
362  client_id)));
363 }
364 
365 std::vector<uint8_t>
366 TestControl::generateDuid(uint8_t& randomized) {
367  std::vector<uint8_t> mac_addr(generateMacAddress(randomized));
368  const CommandOptions::MacAddrsVector& macs = options_.getMacsFromFile();
369  // pick a random mac address if we are using option -M..
370  if (macs.size() > 0) {
371  uint16_t r = number_generator_();
372  if (r >= macs.size()) {
373  r = 0;
374  }
375  std::vector<uint8_t> mac = macs[r];
376  // DUID_LL is in this format
377  // 0 1 2 3
378  // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
379  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
380  // | 3 | hardware type (16 bits) |
381  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
382  // . .
383  // . link-layer address (variable length) .
384  // . .
385  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
386 
387  // No C++11 so initializer list support, building a vector<uint8_t> is a
388  // pain...
389  uint8_t duid_ll[] = {0, 3, 0, 1, 0, 0, 0, 0, 0, 0};
390  // copy duid_ll array into the vector
391  std::vector<uint8_t> duid(duid_ll,
392  duid_ll + sizeof(duid_ll) / sizeof(duid_ll[0]));
393  // put the mac address bytes at the end
394  std::copy(mac.begin(), mac.end(), duid.begin() + 4);
395  return (duid);
396  } else {
397  uint32_t clients_num = options_.getClientsNum();
398  if ((clients_num == 0) || (clients_num == 1)) {
399  return (options_.getDuidTemplate());
400  }
401  // Get the base DUID. We are going to randomize part of it.
402  std::vector<uint8_t> duid(options_.getDuidTemplate());
404  duid.resize(duid.size());
405  std::copy(mac_addr.begin(), mac_addr.end(),
406  duid.begin() + duid.size() - mac_addr.size());
407  return (duid);
408  }
409 }
410 
411 int
412 TestControl::getElapsedTimeOffset() const {
413  int elp_offset = options_.getIpVersion() == 4 ?
414  DHCPV4_ELAPSED_TIME_OFFSET : DHCPV6_ELAPSED_TIME_OFFSET;
415  if (options_.getElapsedTimeOffset() > 0) {
416  elp_offset = options_.getElapsedTimeOffset();
417  }
418  return (elp_offset);
419 }
420 
421 template<class T>
422 uint32_t
423 TestControl::getElapsedTime(const T& pkt1, const T& pkt2) {
424  using namespace boost::posix_time;
425  ptime pkt1_time = pkt1->getTimestamp();
426  ptime pkt2_time = pkt2->getTimestamp();
427  if (pkt1_time.is_not_a_date_time() ||
428  pkt2_time.is_not_a_date_time()) {
429  isc_throw(InvalidOperation, "packet timestamp not set");;
430  }
431  time_period elapsed_period(pkt1_time, pkt2_time);
432  return (elapsed_period.is_null() ? 0 :
433  elapsed_period.length().total_milliseconds());
434 }
435 
436 int
437 TestControl::getRandomOffset(const int arg_idx) const {
438  int rand_offset = options_.getIpVersion() == 4 ?
439  DHCPV4_RANDOMIZATION_OFFSET : DHCPV6_RANDOMIZATION_OFFSET;
440  if (options_.getRandomOffset().size() > arg_idx) {
441  rand_offset = options_.getRandomOffset()[arg_idx];
442  }
443  return (rand_offset);
444 }
445 
446 int
447 TestControl::getRequestedIpOffset() const {
448  int rip_offset = options_.getIpVersion() == 4 ?
449  DHCPV4_REQUESTED_IP_OFFSET : DHCPV6_IA_NA_OFFSET;
450  if (options_.getRequestedIpOffset() > 0) {
451  rip_offset = options_.getRequestedIpOffset();
452  }
453  return (rip_offset);
454 }
455 
456 int
457 TestControl::getServerIdOffset() const {
458  int srvid_offset = options_.getIpVersion() == 4 ?
459  DHCPV4_SERVERID_OFFSET : DHCPV6_SERVERID_OFFSET;
460  if (options_.getServerIdOffset() > 0) {
461  srvid_offset = options_.getServerIdOffset();
462  }
463  return (srvid_offset);
464 }
465 
467 TestControl::getTemplateBuffer(const size_t idx) const {
468  if (template_buffers_.size() > idx) {
469  return (template_buffers_[idx]);
470  }
471  isc_throw(OutOfRange, "invalid buffer index");
472 }
473 
474 int
475 TestControl::getTransactionIdOffset(const int arg_idx) const {
476  int xid_offset = options_.getIpVersion() == 4 ?
477  DHCPV4_TRANSID_OFFSET : DHCPV6_TRANSID_OFFSET;
478  if (options_.getTransactionIdOffset().size() > arg_idx) {
479  xid_offset = options_.getTransactionIdOffset()[arg_idx];
480  }
481  return (xid_offset);
482 }
483 
484 void
485 TestControl::handleChild(int) {
486  int status = 0;
487  while (wait3(&status, WNOHANG, NULL) > 0) {
488  // continue
489  }
490 }
491 
492 void
493 TestControl::handleInterrupt(int) {
494  interrupted_ = true;
495 }
496 
497 void
498 TestControl::initPacketTemplates() {
499  template_packets_v4_.clear();
500  template_packets_v6_.clear();
501  template_buffers_.clear();
502  std::vector<std::string> template_files = options_.getTemplateFiles();
503  for (std::vector<std::string>::const_iterator it = template_files.begin();
504  it != template_files.end(); ++it) {
505  readPacketTemplate(*it);
506  }
507 }
508 
509 void
510 TestControl::sendPackets(const uint64_t packets_num,
511  const bool preload /* = false */) {
512  for (uint64_t i = packets_num; i > 0; --i) {
513  if (options_.getIpVersion() == 4) {
514  // No template packets means that no -T option was specified.
515  // We have to build packets ourselves.
516  if (template_buffers_.empty()) {
517  sendDiscover4(preload);
518  } else {
521  sendDiscover4(template_buffers_[0], preload);
522  }
523  } else {
524  // No template packets means that no -T option was specified.
525  // We have to build packets ourselves.
526  if (template_buffers_.empty()) {
527  sendSolicit6(preload);
528  } else {
531  sendSolicit6(template_buffers_[0], preload);
532  }
533  }
534  }
535 }
536 
537 uint64_t
538 TestControl::sendMultipleRequests(const uint64_t msg_num) {
539  for (uint64_t i = 0; i < msg_num; ++i) {
540  if (!sendRequestFromAck()) {
541  return (i);
542  }
543  }
544  return (msg_num);
545 }
546 
547 uint64_t
548 TestControl::sendMultipleMessages6(const uint32_t msg_type,
549  const uint64_t msg_num) {
550  for (uint64_t i = 0; i < msg_num; ++i) {
551  if (!sendMessageFromReply(msg_type)) {
552  return (i);
553  }
554  }
555  return (msg_num);
556 }
557 
558 void
559 TestControl::printDiagnostics() const {
560  if (options_.testDiags('a')) {
561  // Print all command line parameters.
562  options_.printCommandLine();
563  // Print MAC and DUID.
564  std::cout << "Set MAC to " << vector2Hex(options_.getMacTemplate(), "::")
565  << std::endl;
566  if (options_.getDuidTemplate().size() > 0) {
567  std::cout << "Set DUID to " << vector2Hex(options_.getDuidTemplate()) << std::endl;
568  }
569  }
570 }
571 
572 void
573 TestControl::printTemplate(const uint8_t packet_type) const {
574  std::string hex_buf;
575  int arg_idx = 0;
576  if (options_.getIpVersion() == 4) {
577  if (packet_type == DHCPREQUEST) {
578  arg_idx = 1;
579  }
580  std::map<uint8_t, dhcp::Pkt4Ptr>::const_iterator pkt_it =
581  template_packets_v4_.find(packet_type);
582  if ((pkt_it != template_packets_v4_.end()) &&
583  pkt_it->second) {
584  const util::OutputBuffer& out_buf(pkt_it->second->getBuffer());
585  const char* out_buf_data =
586  static_cast<const char*>(out_buf.getData());
587  std::vector<uint8_t> buf(out_buf_data, out_buf_data + out_buf.getLength());
588  hex_buf = vector2Hex(buf);
589  }
590  } else if (options_.getIpVersion() == 6) {
591  if (packet_type == DHCPV6_REQUEST) {
592  arg_idx = 1;
593  }
594  std::map<uint8_t, dhcp::Pkt6Ptr>::const_iterator pkt_it =
595  template_packets_v6_.find(packet_type);
596  if (pkt_it != template_packets_v6_.end() &&
597  pkt_it->second) {
598  const util::OutputBuffer& out_buf(pkt_it->second->getBuffer());
599  const char* out_buf_data =
600  static_cast<const char*>(out_buf.getData());
601  std::vector<uint8_t> buf(out_buf_data, out_buf_data + out_buf.getLength());
602  hex_buf = vector2Hex(buf);
603  }
604  }
605  std::cout << "xid-offset=" << getTransactionIdOffset(arg_idx) << std::endl;
606  std::cout << "random-offset=" << getRandomOffset(arg_idx) << std::endl;
607  if (arg_idx > 0) {
608  std::cout << "srvid-offset=" << getServerIdOffset() << std::endl;
609  std::cout << "time-offset=" << getElapsedTimeOffset() << std::endl;
610  std::cout << "ip-offset=" << getRequestedIpOffset() << std::endl;
611  }
612 
613  std::cout << "contents: " << std::endl;
614  int line_len = 32;
615  int i = 0;
616  while (line_len == 32) {
617  if (hex_buf.length() - i < 32) {
618  line_len = hex_buf.length() - i;
619  };
620  if (line_len > 0) {
621  std::cout << setfill('0') << setw(4) << std::hex << i << std::dec
622  << " " << hex_buf.substr(i, line_len) << std::endl;
623  }
624  i += 32;
625  }
626  std::cout << std::endl;
627 }
628 
629 void
630 TestControl::printTemplates() const {
631  if (options_.getIpVersion() == 4) {
632  printTemplate(DHCPDISCOVER);
633  printTemplate(DHCPREQUEST);
634  } else if (options_.getIpVersion() == 6) {
635  printTemplate(DHCPV6_SOLICIT);
636  printTemplate(DHCPV6_REQUEST);
637  }
638 }
639 
640 void
641 TestControl::printRate() const {
642  double rate = 0;
643  std::string exchange_name = "4-way exchanges";
644  ExchangeType xchg_type = ExchangeType::DO;
645  if (options_.getIpVersion() == 4) {
646  xchg_type =
647  options_.getExchangeMode() == CommandOptions::DO_SA ?
648  ExchangeType::DO : ExchangeType::RA;
649  if (xchg_type == ExchangeType::DO) {
650  exchange_name = "DISCOVER-OFFER";
651  }
652  } else if (options_.getIpVersion() == 6) {
653  xchg_type =
654  options_.getExchangeMode() == CommandOptions::DO_SA ?
655  ExchangeType::SA : ExchangeType::RR;
656  if (xchg_type == ExchangeType::SA) {
657  exchange_name = options_.isRapidCommit() ? "Solicit-Reply" :
658  "Solicit-Advertise";
659  }
660  }
661  double duration =
662  stats_mgr_.getTestPeriod().length().total_nanoseconds() / 1e9;
663  rate = stats_mgr_.getRcvdPacketsNum(xchg_type) / duration;
664  std::ostringstream s;
665  s << "***Rate statistics***" << std::endl;
666  s << "Rate: " << rate << " " << exchange_name << "/second";
667  if (options_.getRate() > 0) {
668  s << ", expected rate: " << options_.getRate() << std::endl;
669  }
670 
671  std::cout << s.str() << std::endl;
672 
673  std::cout <<"***Malformed Packets***" << std::endl
674  << "Malformed packets: " << ExchangeStats::malformed_pkts_
675  << std::endl;
676 }
677 
678 void
679 TestControl::printIntermediateStats() {
680  int delay = options_.getReportDelay();
681  ptime now = microsec_clock::universal_time();
682  time_period time_since_report(last_report_, now);
683  if (time_since_report.length().total_seconds() >= delay) {
684  stats_mgr_.printIntermediateStats(options_.getCleanReport(),
685  options_.getCleanReportSeparator());
686  last_report_ = now;
687  }
688 }
689 
690 void
691 TestControl::printStats() const {
692  printRate();
693  stats_mgr_.printStats();
694  if (options_.testDiags('i')) {
695  stats_mgr_.printCustomCounters();
696  }
697 }
698 
699 std::string
700 TestControl::vector2Hex(const std::vector<uint8_t>& vec,
701  const std::string& separator /* = "" */) {
702  std::ostringstream stream;
703  for (std::vector<uint8_t>::const_iterator it = vec.begin();
704  it != vec.end();
705  ++it) {
706  if (it == vec.begin()) {
707  stream << byte2Hex(*it);
708  } else {
709  stream << separator << byte2Hex(*it);
710  }
711  }
712  return (stream.str());
713 }
714 
715 void
716 TestControl::readPacketTemplate(const std::string& file_name) {
717  std::ifstream temp_file;
718  temp_file.open(file_name.c_str(), ios::in | ios::binary | ios::ate);
719  if (!temp_file.is_open()) {
720  isc_throw(BadValue, "unable to open template file " << file_name);
721  }
722  // Read template file contents.
723  std::streampos temp_size = temp_file.tellg();
724  if (temp_size == std::streampos(0)) {
725  temp_file.close();
726  isc_throw(OutOfRange, "the template file " << file_name << " is empty");
727  }
728  temp_file.seekg(0, ios::beg);
729  std::vector<char> file_contents(temp_size);
730  temp_file.read(&file_contents[0], temp_size);
731  temp_file.close();
732  // Spaces are allowed so we have to strip the contents
733  // from them. In the same time we want to make sure that
734  // apart from spaces the file contains hexadecimal digits
735  // only.
736  std::vector<char> hex_digits;
737  for (size_t i = 0; i < file_contents.size(); ++i) {
738  if (isxdigit(file_contents[i])) {
739  hex_digits.push_back(file_contents[i]);
740  } else if (!isxdigit(file_contents[i]) &&
741  !isspace(file_contents[i])) {
742  isc_throw(BadValue, "'" << file_contents[i] << "' is not a"
743  " hexadecimal digit");
744  }
745  }
746  // Expect even number of digits.
747  if (hex_digits.size() % 2 != 0) {
748  isc_throw(OutOfRange, "odd number of digits in template file");
749  } else if (hex_digits.empty()) {
750  isc_throw(OutOfRange, "template file " << file_name << " is empty");
751  }
752  std::vector<uint8_t> binary_stream;
753  for (size_t i = 0; i < hex_digits.size(); i += 2) {
754  stringstream s;
755  s << "0x" << hex_digits[i] << hex_digits[i+1];
756  int b;
757  s >> std::hex >> b;
758  binary_stream.push_back(static_cast<uint8_t>(b));
759  }
760  template_buffers_.push_back(binary_stream);
761 }
762 
763 void
764 TestControl::processReceivedPacket4(const Pkt4Ptr& pkt4) {
765  if (pkt4->getType() == DHCPOFFER) {
766  PktPtr pkt = stats_mgr_.passRcvdPacket(ExchangeType::DO, pkt4);
767  address4Uniqueness(pkt4, ExchangeType::DO);
768  Pkt4Ptr discover_pkt4(boost::dynamic_pointer_cast<Pkt4>(pkt));
769  CommandOptions::ExchangeMode xchg_mode = options_.getExchangeMode();
770  if ((xchg_mode == CommandOptions::DORA_SARR) && discover_pkt4) {
771  if (template_buffers_.size() < 2) {
772  sendRequest4(discover_pkt4, pkt4);
773  } else {
776  sendRequest4(template_buffers_[1], discover_pkt4, pkt4);
777  }
778  }
779  } else if (pkt4->getType() == DHCPACK) {
780  // If received message is DHCPACK, we have to check if this is
781  // a response to 4-way exchange. We'll match this packet with
782  // a DHCPREQUEST sent as part of the 4-way exchanges.
783  if (stats_mgr_.passRcvdPacket(ExchangeType::RA, pkt4)) {
784  address4Uniqueness(pkt4, ExchangeType::RA);
785  // The DHCPACK belongs to DHCPREQUEST-DHCPACK exchange type.
786  // So, we may need to keep this DHCPACK in the storage if renews.
787  // Note that, DHCPACK messages hold the information about
788  // leases assigned. We use this information to renew.
789  if (stats_mgr_.hasExchangeStats(ExchangeType::RNA)) {
790  // Renew messages are sent, because StatsMgr has the
791  // specific exchange type specified. Let's append the DHCPACK.
792  // message to a storage
793  ack_storage_.append(pkt4);
794  }
795  // The DHCPACK message is not a server's response to the DHCPREQUEST
796  // message sent within the 4-way exchange. It may be a response to a
797  // renewal. In this case we first check if StatsMgr has exchange type
798  // for renew specified, and if it has, if there is a corresponding
799  // renew message for the received DHCPACK.
800  } else if (stats_mgr_.hasExchangeStats(ExchangeType::RNA)) {
801  stats_mgr_.passRcvdPacket(ExchangeType::RNA, pkt4);
802  }
803  }
804 }
805 
806 void
807 TestControl::address6Uniqueness(const Pkt6Ptr& pkt6, ExchangeType xchg_type) {
808  // check if received address is unique
809  if (options_.getAddrUnique()) {
810  std::set<std::string> current;
811  // addresses were already checked in validateIA
812  // we can safely assume that those are correct
813  for (OptionCollection::iterator opt = pkt6->options_.begin();
814  opt != pkt6->options_.end(); ++opt) {
815  switch (opt->second->getType()) {
816  case D6O_IA_PD: {
817  // add address and check if it has not been already assigned
818  // addresses should be unique cross options of the packet
819  auto ret = current.emplace(boost::dynamic_pointer_cast<
820  Option6IAPrefix>(opt->second->getOption(D6O_IAPREFIX))->getAddress().toText());
821  if (!ret.second) {
822  stats_mgr_.updateNonUniqueAddrNum(xchg_type);
823  }
824  break;
825  }
826  case D6O_IA_NA: {
827  // add address and check if it has not been already assigned
828  // addresses should be unique cross options of the packet
829  auto ret = current.emplace(boost::dynamic_pointer_cast<
830  Option6IAAddr>(opt->second->getOption(D6O_IAADDR))->getAddress().toText());
831  if (!ret.second) {
832  stats_mgr_.updateNonUniqueAddrNum(xchg_type);
833  }
834  break;
835  }
836  default:
837  break;
838  }
839  }
840  // addresses should be unique cross packets
841  addUniqeAddr(current, xchg_type);
842  }
843 }
844 
845 void
846 TestControl::address4Uniqueness(const Pkt4Ptr& pkt4, ExchangeType xchg_type) {
847  // check if received address is unique
848  if (options_.getAddrUnique()) {
849  // addresses were already checked in validateIA
850  // we can safely assume that those are correct
851  std::set<std::string> current;
852  current.insert(pkt4->getYiaddr().toText());
853  // addresses should be unique cross packets
854  addUniqeAddr(current, xchg_type);
855  }
856 }
857 
858 bool
859 TestControl::validateIA(const Pkt6Ptr& pkt6) {
860  // check if iaaddr exists - if it does, we can continue sending request
861  // if not we will update statistics about rejected leases
862  // @todo it's checking just one iaaddress option for now it's ok
863  // but when perfdhcp will be extended to create message with multiple IA
864  // this will have to be iterate on:
865  // OptionCollection ias = pkt6->getOptions(D6O_IA_NA);
866  Option6IAPrefixPtr iapref;
867  Option6IAAddrPtr iaaddr;
868  if (pkt6->getOption(D6O_IA_PD)) {
869  iapref = boost::dynamic_pointer_cast<
870  Option6IAPrefix>(pkt6->getOption(D6O_IA_PD)->getOption(D6O_IAPREFIX));
871  }
872  if (pkt6->getOption(D6O_IA_NA)) {
873  iaaddr = boost::dynamic_pointer_cast<
874  Option6IAAddr>(pkt6->getOption(D6O_IA_NA)->getOption(D6O_IAADDR));
875  }
876 
877  bool address_and_prefix = options_.getLeaseType().includes(
878  CommandOptions::LeaseType::ADDRESS_AND_PREFIX);
879  bool prefix_only = options_.getLeaseType().includes(
880  CommandOptions::LeaseType::PREFIX);
881  bool address_only = options_.getLeaseType().includes(
882  CommandOptions::LeaseType::ADDRESS);
883  if ((address_and_prefix && iapref && iaaddr) ||
884  (prefix_only && iapref && !address_and_prefix) ||
885  (address_only && iaaddr && !address_and_prefix)) {
886  return true;
887  } else {
888  return false;
889  }
890 }
891 
892 void
893 TestControl::processReceivedPacket6(const Pkt6Ptr& pkt6) {
894  uint8_t packet_type = pkt6->getType();
895  if (packet_type == DHCPV6_ADVERTISE) {
896  PktPtr pkt = stats_mgr_.passRcvdPacket(ExchangeType::SA, pkt6);
897  Pkt6Ptr solicit_pkt6(boost::dynamic_pointer_cast<Pkt6>(pkt));
898  CommandOptions::ExchangeMode xchg_mode = options_.getExchangeMode();
899  if ((xchg_mode == CommandOptions::DORA_SARR) && solicit_pkt6) {
900  if (validateIA(pkt6)) {
901  // if address is correct - check uniqueness
902  address6Uniqueness(pkt6, ExchangeType::SA);
903  if (template_buffers_.size() < 2) {
904  sendRequest6(pkt6);
905  } else {
908  sendRequest6(template_buffers_[1], pkt6);
909  }
910  } else {
911  stats_mgr_.updateRejLeases(ExchangeType::SA);
912  }
913  }
914  } else if (packet_type == DHCPV6_REPLY) {
915  // If the received message is Reply, we have to find out which exchange
916  // type the Reply message belongs to. It is doable by matching the Reply
917  // transaction id with the transaction id of the sent Request, Renew
918  // or Release. First we start with the Request.
919  if (stats_mgr_.passRcvdPacket(ExchangeType::RR, pkt6)) {
920  // The Reply belongs to Request-Reply exchange type. So, we may need
921  // to keep this Reply in the storage if Renews or/and Releases are
922  // being sent. Note that, Reply messages hold the information about
923  // leases assigned. We use this information to construct Renew and
924  // Release messages.
925  if (validateIA(pkt6)) {
926  // if address is correct - check uniqueness
927  address6Uniqueness(pkt6, ExchangeType::RR);
928  // check if there is correct IA to continue with Renew/Release
929  if (stats_mgr_.hasExchangeStats(ExchangeType::RN) ||
930  stats_mgr_.hasExchangeStats(ExchangeType::RL)) {
931  // Renew or Release messages are sent, because StatsMgr has the
932  // specific exchange type specified. Let's append the Reply
933  // message to a storage.
934  reply_storage_.append(pkt6);
935  }
936  } else {
937  stats_mgr_.updateRejLeases(ExchangeType::RR);
938  }
939  // The Reply message is not a server's response to the Request message
940  // sent within the 4-way exchange. It may be a response to the Renew
941  // or Release message. In the if clause we first check if StatsMgr
942  // has exchange type for Renew specified, and if it has, if there is
943  // a corresponding Renew message for the received Reply. If not,
944  // we check that StatsMgr has exchange type for Release specified,
945  // as possibly the Reply has been sent in response to Release.
946  } else if (!(stats_mgr_.hasExchangeStats(ExchangeType::RN) &&
947  stats_mgr_.passRcvdPacket(ExchangeType::RN, pkt6)) &&
948  stats_mgr_.hasExchangeStats(ExchangeType::RL)) {
949  // At this point, it is only possible that the Reply has been sent
950  // in response to a Release. Try to match the Reply with Release.
951  stats_mgr_.passRcvdPacket(ExchangeType::RL, pkt6);
952  }
953  }
954 }
955 
956 unsigned int
957 TestControl::consumeReceivedPackets() {
958  unsigned int pkt_count = 0;
959  PktPtr pkt;
960  while ((pkt = receiver_.getPkt())) {
961  pkt_count += 1;
962  if (options_.getIpVersion() == 4) {
963  Pkt4Ptr pkt4 = boost::dynamic_pointer_cast<Pkt4>(pkt);
964  processReceivedPacket4(pkt4);
965  } else {
966  Pkt6Ptr pkt6 = boost::dynamic_pointer_cast<Pkt6>(pkt);
967  processReceivedPacket6(pkt6);
968  }
969  }
970  return pkt_count;
971 }
972 void
973 TestControl::registerOptionFactories4() const {
974  static bool factories_registered = false;
975  if (!factories_registered) {
976  // DHCP_MESSAGE_TYPE option factory.
977  LibDHCP::OptionFactoryRegister(Option::V4,
979  &TestControl::factoryGeneric);
980  // DHCP_SERVER_IDENTIFIER option factory.
981  LibDHCP::OptionFactoryRegister(Option::V4,
983  &TestControl::factoryGeneric);
984  // DHCP_PARAMETER_REQUEST_LIST option factory.
985  LibDHCP::OptionFactoryRegister(Option::V4,
987  &TestControl::factoryRequestList4);
988  }
989  factories_registered = true;
990 }
991 
992 void
993 TestControl::registerOptionFactories6() const {
994  static bool factories_registered = false;
995  if (!factories_registered) {
996  // D6O_ELAPSED_TIME
997  LibDHCP::OptionFactoryRegister(Option::V6,
999  &TestControl::factoryElapsedTime6);
1000  // D6O_RAPID_COMMIT
1001  LibDHCP::OptionFactoryRegister(Option::V6,
1003  &TestControl::factoryRapidCommit6);
1004  // D6O_ORO (option request option) factory.
1005  LibDHCP::OptionFactoryRegister(Option::V6,
1006  D6O_ORO,
1007  &TestControl::factoryOptionRequestOption6);
1008  // D6O_CLIENTID option factory.
1009  LibDHCP::OptionFactoryRegister(Option::V6,
1010  D6O_CLIENTID,
1011  &TestControl::factoryGeneric);
1012  // D6O_SERVERID option factory.
1013  LibDHCP::OptionFactoryRegister(Option::V6,
1014  D6O_SERVERID,
1015  &TestControl::factoryGeneric);
1016  // D6O_IA_NA option factory.
1017  LibDHCP::OptionFactoryRegister(Option::V6,
1018  D6O_IA_NA,
1019  &TestControl::factoryIana6);
1020 
1021  // D6O_IA_PD option factory.
1022  LibDHCP::OptionFactoryRegister(Option::V6,
1023  D6O_IA_PD,
1024  &TestControl::factoryIapd6);
1025 
1026 
1027  }
1028  factories_registered = true;
1029 }
1030 
1031 void
1032 TestControl::registerOptionFactories() const {
1033  switch(options_.getIpVersion()) {
1034  case 4:
1035  registerOptionFactories4();
1036  break;
1037  case 6:
1038  registerOptionFactories6();
1039  break;
1040  default:
1041  isc_throw(InvalidOperation, "command line options have to be parsed "
1042  "before DHCP option factories can be registered");
1043  }
1044 }
1045 
1046 void
1047 TestControl::reset() {
1048  transid_gen_.reset();
1049  last_report_ = microsec_clock::universal_time();
1050  // Actual generators will have to be set later on because we need to
1051  // get command line parameters first.
1052  setTransidGenerator(NumberGeneratorPtr());
1053  setMacAddrGenerator(NumberGeneratorPtr());
1054  first_packet_serverid_.clear();
1055  interrupted_ = false;
1056 }
1057 
1058 TestControl::TestControl(CommandOptions& options, BasePerfSocket &socket) :
1059  exit_time_(not_a_date_time),
1060  number_generator_(0, options.getMacsFromFile().size()),
1061  socket_(socket),
1062  receiver_(socket, options.isSingleThreaded(), options.getIpVersion()),
1063  stats_mgr_(options),
1064  options_(options)
1065 {
1066  // Reset singleton state before test starts.
1067  reset();
1068 
1069  // Ip version is not set ONLY in case the command options
1070  // were not parsed. This surely means that parse() function
1071  // was not called prior to starting the test. This is fatal
1072  // error.
1073  if (options_.getIpVersion() == 0) {
1075  "command options must be parsed before running a test");
1076  } else if (options_.getIpVersion() == 4) {
1077  // Turn off packet queueing.
1078  IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, data::ElementPtr());
1080  } else {
1081  // Turn off packet queueing.
1082  IfaceMgr::instance().configureDHCPPacketQueue(AF_INET6, data::ElementPtr());
1084  }
1085 
1086  uint32_t clients_num = options_.getClientsNum() == 0 ?
1087  1 : options_.getClientsNum();
1089 
1090  // Diagnostics are command line options mainly.
1091  printDiagnostics();
1092  // Option factories have to be registered.
1094  // Initialize packet templates.
1096  // Initialize randomization seed.
1097  if (options_.isSeeded()) {
1098  srandom(options_.getSeed());
1099  } else {
1100  // Seed with current time.
1101  time_period duration(from_iso_string("20111231T235959"),
1102  microsec_clock::universal_time());
1103  srandom(duration.length().total_seconds()
1104  + duration.length().fractional_seconds());
1105  }
1106  // If user interrupts the program we will exit gracefully.
1107  signal(SIGINT, TestControl::handleInterrupt);
1108 }
1109 
1110 void
1111 TestControl::runWrapped(bool do_stop /*= false */) const {
1112  if (!options_.getWrapped().empty()) {
1113  pid_t pid = 0;
1114  signal(SIGCHLD, handleChild);
1115  pid = fork();
1116  if (pid < 0) {
1117  isc_throw(Unexpected, "unable to fork");
1118  } else if (pid == 0) {
1119  execlp(options_.getWrapped().c_str(), do_stop ? "stop" : "start", (void*)0);
1120  }
1121  }
1122 }
1123 
1124 void
1126  if (options_.testDiags('T')) {
1127  if (template_packets_v4_.find(pkt->getType()) == template_packets_v4_.end()) {
1128  template_packets_v4_[pkt->getType()] = pkt;
1129  }
1130  }
1131 }
1132 
1133 void
1135  if (options_.testDiags('T')) {
1136  if (template_packets_v6_.find(pkt->getType()) == template_packets_v6_.end()) {
1137  template_packets_v6_[pkt->getType()] = pkt;
1138  }
1139  }
1140 }
1141 
1142 void
1143 TestControl::sendDiscover4(const bool preload /*= false*/) {
1144  // Generate the MAC address to be passed in the packet.
1145  uint8_t randomized = 0;
1146  std::vector<uint8_t> mac_address = generateMacAddress(randomized);
1147  // Generate transaction id to be set for the new exchange.
1148  const uint32_t transid = generateTransid();
1149  Pkt4Ptr pkt4(new Pkt4(DHCPDISCOVER, transid));
1150  if (!pkt4) {
1151  isc_throw(Unexpected, "failed to create DISCOVER packet");
1152  }
1153 
1154  // Delete the default Message Type option set by Pkt4
1155  pkt4->delOption(DHO_DHCP_MESSAGE_TYPE);
1156 
1157  // Set options: DHCP_MESSAGE_TYPE and DHCP_PARAMETER_REQUEST_LIST
1158  OptionBuffer buf_msg_type;
1159  buf_msg_type.push_back(DHCPDISCOVER);
1160  pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_MESSAGE_TYPE,
1161  buf_msg_type));
1162  pkt4->addOption(Option::factory(Option::V4,
1164 
1165 
1166  // Set client's and server's ports as well as server's address,
1167  // and local (relay) address.
1168  setDefaults4(pkt4);
1169 
1170  // Set hardware address
1171  pkt4->setHWAddr(HTYPE_ETHER, mac_address.size(), mac_address);
1172 
1173  // Set client identifier
1174  pkt4->addOption(generateClientId(pkt4->getHWAddr()));
1175 
1176  // Check if we need to simulate HA failures by pretending no responses were received.
1177  // The DHCP protocol signals that by increasing secs field (seconds since the configuration attempt started).
1179  stats_mgr_.getTestPeriod().length().total_seconds() >= options_.getWaitForElapsedTime() &&
1180  stats_mgr_.getTestPeriod().length().total_seconds() < options_.getWaitForElapsedTime() +
1182 
1183  // Keep increasing elapsed time. The value should start increasing steadily.
1184  uint32_t val = stats_mgr_.getTestPeriod().length().total_seconds() - options_.getWaitForElapsedTime() + 1;
1185  if (val > 65535) {
1186  val = 65535;
1187  }
1188  pkt4->setSecs(static_cast<uint16_t>(val));
1189  }
1190 
1191  // Add any extra options that user may have specified.
1192  addExtraOpts(pkt4);
1193 
1194  pkt4->pack();
1195  socket_.send(pkt4);
1196  if (!preload) {
1198  }
1199 
1200  saveFirstPacket(pkt4);
1201 }
1202 
1203 void
1204 TestControl::sendDiscover4(const std::vector<uint8_t>& template_buf,
1205  const bool preload /* = false */) {
1206  // Get the first argument if multiple the same arguments specified
1207  // in the command line. First one refers to DISCOVER packets.
1208  const uint8_t arg_idx = 0;
1209  // Generate the MAC address to be passed in the packet.
1210  uint8_t randomized = 0;
1211  std::vector<uint8_t> mac_address = generateMacAddress(randomized);
1212  // Generate transaction id to be set for the new exchange.
1213  const uint32_t transid = generateTransid();
1214  // Get transaction id offset.
1215  size_t transid_offset = getTransactionIdOffset(arg_idx);
1216  // Get randomization offset.
1217  // We need to go back by HW_ETHER_LEN (MAC address length)
1218  // because this offset points to last octet of MAC address.
1219  size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
1220  // Create temporary buffer with template contents. We will
1221  // modify this temporary buffer but we don't want to modify
1222  // the original template.
1223  std::vector<uint8_t> in_buf(template_buf.begin(),
1224  template_buf.end());
1225  // Check if we are not going out of bounds.
1226  if (rand_offset + HW_ETHER_LEN > in_buf.size()) {
1227  isc_throw(OutOfRange, "randomization offset is out of bounds");
1228  }
1229  PerfPkt4Ptr pkt4(new PerfPkt4(&in_buf[0], in_buf.size(),
1230  transid_offset,
1231  transid));
1232 
1233  // Replace MAC address in the template with actual MAC address.
1234  pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
1235  // Create a packet from the temporary buffer.
1236  setDefaults4(boost::static_pointer_cast<Pkt4>(pkt4));
1237  // Pack the input packet buffer to output buffer so as it can
1238  // be sent to server.
1239  pkt4->rawPack();
1240  socket_.send(boost::static_pointer_cast<Pkt4>(pkt4));
1241  if (!preload) {
1242  // Update packet stats.
1244  boost::static_pointer_cast<Pkt4>(pkt4));
1245  }
1246  saveFirstPacket(pkt4);
1247 }
1248 
1249 bool
1251  // Get one of the recorded DHCPACK messages.
1252  Pkt4Ptr ack = ack_storage_.getRandom();
1253  if (!ack) {
1254  return (false);
1255  }
1256 
1257  // Create message of the specified type.
1258  Pkt4Ptr msg = createRequestFromAck(ack);
1259  setDefaults4(msg);
1260 
1261  // Override relay address
1262  msg->setGiaddr(ack->getGiaddr());
1263 
1264  // Add any extra options that user may have specified.
1265  addExtraOpts(msg);
1266 
1267  msg->pack();
1268  // And send it.
1269  socket_.send(msg);
1271  return (true);
1272 }
1273 
1274 
1275 bool
1276 TestControl::sendMessageFromReply(const uint16_t msg_type) {
1277  // We only permit Release or Renew messages to be sent using this function.
1278  if (msg_type != DHCPV6_RENEW && msg_type != DHCPV6_RELEASE) {
1279  isc_throw(isc::BadValue, "invalid message type " << msg_type
1280  << " to be sent, expected DHCPV6_RENEW or DHCPV6_RELEASE");
1281  }
1282  Pkt6Ptr reply = reply_storage_.getRandom();
1283  if (!reply) {
1284  return (false);
1285  }
1286  // Prepare the message of the specified type.
1287  Pkt6Ptr msg = createMessageFromReply(msg_type, reply);
1288  setDefaults6(msg);
1289 
1290  // Add any extra options that user may have specified.
1291  addExtraOpts(msg);
1292 
1293  msg->pack();
1294  // And send it.
1295  socket_.send(msg);
1298  : ExchangeType::RL), msg);
1299  return (true);
1300 }
1301 
1302 void
1304  const dhcp::Pkt4Ptr& offer_pkt4) {
1305  // Use the same transaction id as the one used in the discovery packet.
1306  const uint32_t transid = discover_pkt4->getTransid();
1307  Pkt4Ptr pkt4(new Pkt4(DHCPREQUEST, transid));
1308 
1309  // Use first flags indicates that we want to use the server
1310  // id captured in first packet.
1311  if (options_.isUseFirst() &&
1312  (first_packet_serverid_.size() > 0)) {
1313  pkt4->addOption(Option::factory(Option::V4, DHO_DHCP_SERVER_IDENTIFIER,
1315  } else {
1316  OptionPtr opt_serverid =
1317  offer_pkt4->getOption(DHO_DHCP_SERVER_IDENTIFIER);
1318  if (!opt_serverid) {
1319  isc_throw(BadValue, "there is no SERVER_IDENTIFIER option "
1320  << "in OFFER message");
1321  }
1323  first_packet_serverid_ = opt_serverid->getData();
1324  }
1325  pkt4->addOption(opt_serverid);
1326  }
1327 
1329  asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr();
1330  if (!yiaddr.isV4()) {
1331  isc_throw(BadValue, "the YIADDR returned in OFFER packet is not "
1332  " IPv4 address");
1333  }
1334  OptionPtr opt_requested_address =
1336  OptionBuffer()));
1337  opt_requested_address->setUint32(yiaddr.toUint32());
1338  pkt4->addOption(opt_requested_address);
1339  OptionPtr opt_parameter_list =
1340  Option::factory(Option::V4, DHO_DHCP_PARAMETER_REQUEST_LIST);
1341  pkt4->addOption(opt_parameter_list);
1342  // Set client's and server's ports as well as server's address
1343  setDefaults4(pkt4);
1344  // Override relay address
1345  pkt4->setGiaddr(offer_pkt4->getGiaddr());
1346  // Add any extra options that user may have specified.
1347  addExtraOpts(pkt4);
1348 
1349  // Set hardware address
1350  pkt4->setHWAddr(offer_pkt4->getHWAddr());
1351  // Set client id.
1352  pkt4->addOption(generateClientId(pkt4->getHWAddr()));
1353  // Set elapsed time.
1354  uint32_t elapsed_time = getElapsedTime<Pkt4Ptr>(discover_pkt4, offer_pkt4);
1355  pkt4->setSecs(static_cast<uint16_t>(elapsed_time / 1000));
1356  // Prepare on wire data to send.
1357  pkt4->pack();
1358  socket_.send(pkt4);
1360  saveFirstPacket(pkt4);
1361 }
1362 
1363 void
1364 TestControl::sendRequest4(const std::vector<uint8_t>& template_buf,
1365  const dhcp::Pkt4Ptr& discover_pkt4,
1366  const dhcp::Pkt4Ptr& offer_pkt4) {
1367  // Get the second argument if multiple the same arguments specified
1368  // in the command line. Second one refers to REQUEST packets.
1369  const uint8_t arg_idx = 1;
1370  // Use the same transaction id as the one used in the discovery packet.
1371  const uint32_t transid = discover_pkt4->getTransid();
1372  // Get transaction id offset.
1373  size_t transid_offset = getTransactionIdOffset(arg_idx);
1374  // Get the offset of MAC's last octet.
1375  // We need to go back by HW_ETHER_LEN (MAC address length)
1376  // because this offset points to last octet of MAC address.
1377  size_t rand_offset = getRandomOffset(arg_idx) - HW_ETHER_LEN + 1;
1378  // Create temporary buffer from the template.
1379  std::vector<uint8_t> in_buf(template_buf.begin(),
1380  template_buf.end());
1381  // Check if given randomization offset is not out of bounds.
1382  if (rand_offset + HW_ETHER_LEN > in_buf.size()) {
1383  isc_throw(OutOfRange, "randomization offset is out of bounds");
1384  }
1385 
1386  // Create packet from the temporary buffer.
1387  PerfPkt4Ptr pkt4(new PerfPkt4(&in_buf[0], in_buf.size(),
1388  transid_offset,
1389  transid));
1390 
1391  // Set hardware address from OFFER packet received.
1392  HWAddrPtr hwaddr = offer_pkt4->getHWAddr();
1393  std::vector<uint8_t> mac_address(HW_ETHER_LEN, 0);
1394  uint8_t hw_len = hwaddr->hwaddr_.size();
1395  if (hw_len != 0) {
1396  memcpy(&mac_address[0], &hwaddr->hwaddr_[0],
1397  hw_len > HW_ETHER_LEN ? HW_ETHER_LEN : hw_len);
1398  }
1399  pkt4->writeAt(rand_offset, mac_address.begin(), mac_address.end());
1400 
1401  // Set elapsed time.
1402  size_t elp_offset = getElapsedTimeOffset();
1403  uint32_t elapsed_time = getElapsedTime<Pkt4Ptr>(discover_pkt4, offer_pkt4);
1404  pkt4->writeValueAt<uint16_t>(elp_offset,
1405  static_cast<uint16_t>(elapsed_time / 1000));
1406 
1407  // Get the actual server id offset.
1408  size_t sid_offset = getServerIdOffset();
1409  // Use first flags indicates that we want to use the server
1410  // id captured in first packet.
1411  if (options_.isUseFirst() &&
1412  (first_packet_serverid_.size() > 0)) {
1413  boost::shared_ptr<LocalizedOption>
1414  opt_serverid(new LocalizedOption(Option::V4,
1417  sid_offset));
1418  pkt4->addOption(opt_serverid);
1419  } else {
1420  // Copy the contents of server identifier received in
1421  // OFFER packet to put this into REQUEST.
1422  OptionPtr opt_serverid_offer =
1423  offer_pkt4->getOption(DHO_DHCP_SERVER_IDENTIFIER);
1424  if (!opt_serverid_offer) {
1425  isc_throw(BadValue, "there is no SERVER_IDENTIFIER option "
1426  << "in OFFER message");
1427  }
1428  boost::shared_ptr<LocalizedOption>
1429  opt_serverid(new LocalizedOption(Option::V4,
1431  opt_serverid_offer->getData(),
1432  sid_offset));
1433  pkt4->addOption(opt_serverid);
1435  first_packet_serverid_ = opt_serverid_offer->getData();
1436  }
1437  }
1438 
1440  asiolink::IOAddress yiaddr = offer_pkt4->getYiaddr();
1441  if (!yiaddr.isV4()) {
1442  isc_throw(BadValue, "the YIADDR returned in OFFER packet is not "
1443  " IPv4 address");
1444  }
1445 
1446  // Get the actual offset of requested ip.
1447  size_t rip_offset = getRequestedIpOffset();
1448  // Place requested IP option at specified position (rip_offset).
1449  boost::shared_ptr<LocalizedOption>
1450  opt_requested_ip(new LocalizedOption(Option::V4,
1452  OptionBuffer(),
1453  rip_offset));
1454  // The IOAddress is convertible to uint32_t and returns exactly what we need.
1455  opt_requested_ip->setUint32(yiaddr.toUint32());
1456  pkt4->addOption(opt_requested_ip);
1457 
1458  setDefaults4(boost::static_pointer_cast<Pkt4>(pkt4));
1459 
1460  // Add any extra options that user may have specified.
1461  addExtraOpts(pkt4);
1462 
1463  // Prepare on-wire data.
1464  pkt4->rawPack();
1465  socket_.send(boost::static_pointer_cast<Pkt4>(pkt4));
1466  // Update packet stats.
1468  boost::static_pointer_cast<Pkt4>(pkt4));
1469  saveFirstPacket(pkt4);
1470 }
1471 
1472 void
1473 TestControl::sendRequest6(const Pkt6Ptr& advertise_pkt6) {
1474  const uint32_t transid = generateTransid();
1475  Pkt6Ptr pkt6(new Pkt6(DHCPV6_REQUEST, transid));
1476  // Set elapsed time.
1477  OptionPtr opt_elapsed_time =
1478  Option::factory(Option::V6, D6O_ELAPSED_TIME);
1479  pkt6->addOption(opt_elapsed_time);
1480  // Set client id.
1481  OptionPtr opt_clientid = advertise_pkt6->getOption(D6O_CLIENTID);
1482  if (!opt_clientid) {
1483  isc_throw(Unexpected, "client id not found in received packet");
1484  }
1485  pkt6->addOption(opt_clientid);
1486 
1487  // Use first flags indicates that we want to use the server
1488  // id captured in first packet.
1489  if (options_.isUseFirst() &&
1490  (first_packet_serverid_.size() > 0)) {
1491  pkt6->addOption(Option::factory(Option::V6, D6O_SERVERID,
1493  } else {
1494  OptionPtr opt_serverid = advertise_pkt6->getOption(D6O_SERVERID);
1495  if (!opt_serverid) {
1496  isc_throw(Unexpected, "server id not found in received packet");
1497  }
1499  first_packet_serverid_ = opt_serverid->getData();
1500  }
1501  pkt6->addOption(opt_serverid);
1502  }
1503 
1504  // Copy IA_NA or IA_PD option from the Advertise message to the Request
1505  // message being sent to the server. This will throw exception if the
1506  // option to be copied is not found. Note that this function will copy
1507  // one of IA_NA or IA_PD options, depending on the lease-type value
1508  // specified in the command line.
1509  copyIaOptions(advertise_pkt6, pkt6);
1510 
1511  // Set default packet data.
1512  setDefaults6(pkt6);
1513 
1514  // Add any extra options that user may have specified.
1515  addExtraOpts(pkt6);
1516 
1517  // Prepare on-wire data.
1518  pkt6->pack();
1519  socket_.send(pkt6);
1521  saveFirstPacket(pkt6);
1522 }
1523 
1524 void
1525 TestControl::sendRequest6(const std::vector<uint8_t>& template_buf,
1526  const Pkt6Ptr& advertise_pkt6) {
1527  // Get the second argument if multiple the same arguments specified
1528  // in the command line. Second one refers to REQUEST packets.
1529  const uint8_t arg_idx = 1;
1530  // Generate transaction id.
1531  const uint32_t transid = generateTransid();
1532  // Get transaction id offset.
1533  size_t transid_offset = getTransactionIdOffset(arg_idx);
1534  PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(),
1535  transid_offset, transid));
1536  // Set elapsed time.
1537  size_t elp_offset = getElapsedTimeOffset();
1538  boost::shared_ptr<LocalizedOption>
1539  opt_elapsed_time(new LocalizedOption(Option::V6, D6O_ELAPSED_TIME,
1540  OptionBuffer(), elp_offset));
1541  pkt6->addOption(opt_elapsed_time);
1542 
1543  // Get the actual server id offset.
1544  size_t sid_offset = getServerIdOffset();
1545  // Use first flags indicates that we want to use the server
1546  // id captured in first packet.
1547  if (options_.isUseFirst() &&
1548  (first_packet_serverid_.size() > 0)) {
1549  boost::shared_ptr<LocalizedOption>
1550  opt_serverid(new LocalizedOption(Option::V6,
1551  D6O_SERVERID,
1553  sid_offset));
1554  pkt6->addOption(opt_serverid);
1555 
1556  } else {
1557  // Copy the contents of server identifier received in
1558  // ADVERTISE packet to put this into REQUEST.
1559  OptionPtr opt_serverid_advertise =
1560  advertise_pkt6->getOption(D6O_SERVERID);
1561  if (!opt_serverid_advertise) {
1562  isc_throw(BadValue, "there is no SERVERID option "
1563  << "in ADVERTISE message");
1564  }
1565  boost::shared_ptr<LocalizedOption>
1566  opt_serverid(new LocalizedOption(Option::V6,
1567  D6O_SERVERID,
1568  opt_serverid_advertise->getData(),
1569  sid_offset));
1570  pkt6->addOption(opt_serverid);
1572  first_packet_serverid_ = opt_serverid_advertise->getData();
1573  }
1574  }
1575  // Set IA_NA
1576  boost::shared_ptr<Option6IA> opt_ia_na_advertise =
1577  boost::static_pointer_cast<Option6IA>(advertise_pkt6->getOption(D6O_IA_NA));
1578  if (!opt_ia_na_advertise) {
1579  isc_throw(Unexpected, "DHCPv6 IA_NA option not found in received "
1580  "packet");
1581  }
1582  size_t addr_offset = getRequestedIpOffset();
1583  boost::shared_ptr<LocalizedOption>
1584  opt_ia_na(new LocalizedOption(opt_ia_na_advertise, addr_offset));
1585  if (!opt_ia_na->valid()) {
1586  isc_throw(BadValue, "Option IA_NA in advertise packet is invalid");
1587  }
1588  pkt6->addOption(opt_ia_na);
1589  // Set server id.
1590  OptionPtr opt_serverid_advertise = advertise_pkt6->getOption(D6O_SERVERID);
1591  if (!opt_serverid_advertise) {
1592  isc_throw(Unexpected, "DHCPV6 SERVERID option not found in received "
1593  "packet");
1594  }
1595  size_t srvid_offset = getServerIdOffset();
1596  boost::shared_ptr<LocalizedOption>
1597  opt_serverid(new LocalizedOption(Option::V6, D6O_SERVERID,
1598  opt_serverid_advertise->getData(),
1599  srvid_offset));
1600  pkt6->addOption(opt_serverid);
1601  // Get randomization offset.
1602  size_t rand_offset = getRandomOffset(arg_idx);
1603  OptionPtr opt_clientid_advertise = advertise_pkt6->getOption(D6O_CLIENTID);
1604  if (!opt_clientid_advertise) {
1605  isc_throw(Unexpected, "DHCPV6 CLIENTID option not found in received packet");
1606  }
1607  rand_offset -= (opt_clientid_advertise->len() - 1);
1608  // Set client id.
1609  boost::shared_ptr<LocalizedOption>
1610  opt_clientid(new LocalizedOption(Option::V6, D6O_CLIENTID,
1611  opt_clientid_advertise->getData(),
1612  rand_offset));
1613  pkt6->addOption(opt_clientid);
1614  // Set default packet data.
1615  setDefaults6(pkt6);
1616 
1617  // Add any extra options that user may have specified.
1618  addExtraOpts(pkt6);
1619 
1620  // Prepare on wire data.
1621  pkt6->rawPack();
1622  // Send packet.
1623  socket_.send(pkt6);
1624  // Update packet stats.
1626 
1627  // When 'T' diagnostics flag is specified it means that user requested
1628  // printing packet contents. It will be just one (first) packet which
1629  // contents will be printed. Here we check if this packet has been already
1630  // collected. If it hasn't we save this packet so as we can print its
1631  // contents when test is finished.
1632  if (options_.testDiags('T') &&
1635  }
1636 }
1637 
1638 void
1639 TestControl::sendSolicit6(const bool preload /*= false*/) {
1640  // Generate DUID to be passed to the packet
1641  uint8_t randomized = 0;
1642  std::vector<uint8_t> duid = generateDuid(randomized);
1643  // Generate transaction id to be set for the new exchange.
1644  const uint32_t transid = generateTransid();
1645  Pkt6Ptr pkt6(new Pkt6(DHCPV6_SOLICIT, transid));
1646  if (!pkt6) {
1647  isc_throw(Unexpected, "failed to create SOLICIT packet");
1648  }
1649 
1650  // Check if we need to simulate HA failures by pretending no responses were received.
1651  // The DHCPv6 protocol signals that by increasing the elapsed option field. Note it is in 1/100 of a second.
1653  stats_mgr_.getTestPeriod().length().total_seconds() >= options_.getWaitForElapsedTime() &&
1654  stats_mgr_.getTestPeriod().length().total_seconds() < options_.getWaitForElapsedTime() +
1656 
1657 
1658  // Keep increasing elapsed time. The value should start increasing steadily.
1659  uint32_t val = (stats_mgr_.getTestPeriod().length().total_seconds() - options_.getWaitForElapsedTime() + 1)*100;
1660  if (val > 65535) {
1661  val = 65535;
1662  }
1663  OptionPtr elapsed(new OptionInt<uint16_t>(Option::V6, D6O_ELAPSED_TIME, val));
1664  pkt6->addOption(elapsed);
1665  } else {
1666  pkt6->addOption(Option::factory(Option::V6, D6O_ELAPSED_TIME));
1667  }
1668 
1669  if (options_.isRapidCommit()) {
1670  pkt6->addOption(Option::factory(Option::V6, D6O_RAPID_COMMIT));
1671  }
1672  pkt6->addOption(Option::factory(Option::V6, D6O_CLIENTID, duid));
1673  pkt6->addOption(Option::factory(Option::V6, D6O_ORO));
1674 
1675 
1676  // Depending on the lease-type option specified, we should request
1677  // IPv6 address (with IA_NA) or IPv6 prefix (IA_PD) or both.
1678 
1679  // IA_NA
1681  pkt6->addOption(Option::factory(Option::V6, D6O_IA_NA));
1682  }
1683  // IA_PD
1685  pkt6->addOption(Option::factory(Option::V6, D6O_IA_PD));
1686  }
1687 
1688  setDefaults6(pkt6);
1689 
1690  // Add any extra options that user may have specified.
1691  addExtraOpts(pkt6);
1692 
1693  pkt6->pack();
1694  socket_.send(pkt6);
1695  if (!preload) {
1697  }
1698 
1699  saveFirstPacket(pkt6);
1700 }
1701 
1702 void
1703 TestControl::sendSolicit6(const std::vector<uint8_t>& template_buf,
1704  const bool preload /*= false*/) {
1705  const int arg_idx = 0;
1706  // Get transaction id offset.
1707  size_t transid_offset = getTransactionIdOffset(arg_idx);
1708  // Generate transaction id to be set for the new exchange.
1709  const uint32_t transid = generateTransid();
1710  // Create packet.
1711  PerfPkt6Ptr pkt6(new PerfPkt6(&template_buf[0], template_buf.size(),
1712  transid_offset, transid));
1713  if (!pkt6) {
1714  isc_throw(Unexpected, "failed to create SOLICIT packet");
1715  }
1716  size_t rand_offset = getRandomOffset(arg_idx);
1717  // randomized will pick number of bytes randomized so we can
1718  // just use part of the generated duid and substitute a few bytes
1720  uint8_t randomized = 0;
1721  std::vector<uint8_t> duid = generateDuid(randomized);
1722  if (rand_offset > template_buf.size()) {
1723  isc_throw(OutOfRange, "randomization offset is out of bounds");
1724  }
1725  // Store random part of the DUID into the packet.
1726  pkt6->writeAt(rand_offset - randomized + 1,
1727  duid.end() - randomized, duid.end());
1728 
1729  // Prepare on-wire data.
1730  pkt6->rawPack();
1731  setDefaults6(pkt6);
1732 
1733  // Add any extra options that user may have specified.
1734  addExtraOpts(pkt6);
1735 
1736  // Send solicit packet.
1737  socket_.send(pkt6);
1738  if (!preload) {
1739  // Update packet stats.
1741  }
1742  saveFirstPacket(pkt6);
1743 }
1744 
1745 
1746 void
1748  // Interface name.
1749  IfacePtr iface = socket_.getIface();
1750  if (iface == NULL) {
1751  isc_throw(BadValue, "unable to find interface with given index");
1752  }
1753  pkt->setIface(iface->getName());
1754  // Interface index.
1755  pkt->setIndex(socket_.ifindex_);
1756  // Local client's port (68)
1757  pkt->setLocalPort(DHCP4_CLIENT_PORT);
1758  // Server's port (67)
1759  if (options_.getRemotePort()) {
1760  pkt->setRemotePort(options_.getRemotePort());
1761  } else {
1762  pkt->setRemotePort(DHCP4_SERVER_PORT);
1763  }
1764  // The remote server's name or IP.
1765  pkt->setRemoteAddr(IOAddress(options_.getServerName()));
1766  // Set local address.
1767  pkt->setLocalAddr(IOAddress(socket_.addr_));
1768  // Set relay (GIADDR) address to local address if multiple
1769  // subnet mode is not enabled
1770  if (!options_.checkMultiSubnet()) {
1771  pkt->setGiaddr(IOAddress(socket_.addr_));
1772  } else {
1773  pkt->setGiaddr(IOAddress(options_.getRandRelayAddr()));
1774  }
1775  // Pretend that we have one relay (which is us).
1776  pkt->setHops(1);
1777 }
1778 
1779 void
1781  // Interface name.
1782  IfacePtr iface = socket_.getIface();
1783  if (iface == NULL) {
1784  isc_throw(BadValue, "unable to find interface with given index");
1785  }
1786  pkt->setIface(iface->getName());
1787  // Interface index.
1788  pkt->setIndex(socket_.ifindex_);
1789  // Local client's port (547)
1790  pkt->setLocalPort(DHCP6_CLIENT_PORT);
1791  // Server's port (548)
1792  if (options_.getRemotePort()) {
1793  pkt->setRemotePort(options_.getRemotePort());
1794  } else {
1795  pkt->setRemotePort(DHCP6_SERVER_PORT);
1796  }
1797  // Set local address.
1798  pkt->setLocalAddr(socket_.addr_);
1799  // The remote server's name or IP.
1800  pkt->setRemoteAddr(IOAddress(options_.getServerName()));
1801 
1802  // only act as a relay agent when told so.
1805  if (options_.isUseRelayedV6()) {
1806  Pkt6::RelayInfo relay_info;
1807  relay_info.msg_type_ = DHCPV6_RELAY_FORW;
1808  relay_info.hop_count_ = 0;
1809  if (options_.checkMultiSubnet()) {
1810  relay_info.linkaddr_ = IOAddress(options_.getRandRelayAddr());
1811  } else {
1812  relay_info.linkaddr_ = IOAddress(socket_.addr_);
1813  }
1814  relay_info.peeraddr_ = IOAddress(socket_.addr_);
1815  pkt->addRelayInfo(relay_info);
1816  }
1817 }
1818 
1819 namespace {
1820 
1821 static OptionBuffer const concatenateBuffers(OptionBuffer const& a,
1822  OptionBuffer const& b) {
1823  OptionBuffer result;
1824  result.insert(result.end(), a.begin(), a.end());
1825  result.insert(result.end(), b.begin(), b.end());
1826  return result;
1827 }
1828 
1829 static void mergeOptionIntoPacket(Pkt4Ptr const& packet,
1830  OptionPtr const& extra_option) {
1831  uint16_t const code(extra_option->getType());
1832  // If option already exists...
1833  OptionPtr const& option(packet->getOption(code));
1834  if (option) {
1835  switch (code) {
1836  // List here all the options for which we want to concatenate buffers.
1838  packet->delOption(code);
1839  packet->addOption(boost::make_shared<Option>(
1840  Option::V4, code,
1841  concatenateBuffers(option->getData(),
1842  extra_option->getData())));
1843  return;
1844  default:
1845  // For all others, add option as usual, it will result in "Option
1846  // already present in this message" error.
1847  break;
1848  }
1849  }
1850  packet->addOption(extra_option);
1851 }
1852 
1853 } // namespace
1854 
1855 void
1857  // Add all extra options that the user may have specified.
1858  const dhcp::OptionCollection& extra_opts = options_.getExtraOpts();
1859  for (auto entry : extra_opts) {
1860  mergeOptionIntoPacket(pkt, entry.second);
1861  }
1862 }
1863 
1864 void
1866  // Add all extra options that the user may have specified.
1867  const dhcp::OptionCollection& extra_opts = options_.getExtraOpts();
1868  for (auto entry : extra_opts) {
1869  pkt->addOption(entry.second);
1870  }
1871 }
1872 
1873 } // namespace perfdhcp
1874 } // namespace isc
uint64_t getRcvdPacketsNum(const ExchangeType xchg_type) const
Return total number of received packets.
PerfPkt6 (DHCPv6 packet)
Definition: perf_pkt6.h:41
boost::shared_ptr< NumberGenerator > NumberGeneratorPtr
The default generator pointer.
Definition: test_control.h:154
void address6Uniqueness(const dhcp::Pkt6Ptr &pkt6, ExchangeType xchg_type)
Process received v6 addresses uniqueness.
uint32_t generateTransid()
generate transaction id.
Definition: test_control.h:532
boost::shared_ptr< PerfPkt6 > PerfPkt6Ptr
Definition: perf_pkt6.h:127
void sendSolicit6(const bool preload=false)
Send DHCPv6 SOLICIT message.
virtual std::string toText(int indent=0) const
Returns string representation of the option.
DHCP option at specific offset.
bool isUseFirst() const
Check if server-ID to be taken from first package.
LeaseType getLeaseType() const
\ brief Returns the type of lease being requested.
DHCPv6 SOLICIT-ADVERTISE.
ExchangeType
DHCP packet exchange types.
boost::shared_ptr< PerfPkt4 > PerfPkt4Ptr
Definition: perf_pkt4.h:128
void registerOptionFactories() const
Register option factory functions for DHCPv4 or DHCPv6.
void printDiagnostics() const
Print main diagnostics data.
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
PacketStorage< dhcp::Pkt4 > ack_storage_
Storage for DHCPACK messages.
int getRemotePort() const
Returns remote port number.
std::vector< uint8_t > generateDuid(uint8_t &randomized)
Generate DUID.
void runWrapped(bool do_stop=false) const
Run wrapped command.
uint32_t getClientsNum() const
Returns number of simulated clients.
std::vector< std::vector< uint8_t > > MacAddrsVector
A vector holding MAC addresses.
boost::shared_ptr< Iface > IfacePtr
Type definition for the pointer to an Iface object.
Definition: iface_mgr.h:463
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
void setTransidGenerator(const NumberGeneratorPtr &generator)
Set new transaction id generator.
Definition: test_control.h:194
void setDefaults6(const dhcp::Pkt6Ptr &pkt)
Set default DHCPv6 packet parameters.
virtual std::string toText(int indent=0) const
Returns string representation of the option.
StatsMgr stats_mgr_
Statistics Manager.
int getRequestedIpOffset() const
Return requested ip offset in a packet.
bool sendMessageFromReply(const uint16_t msg_type)
Send DHCPv6 Renew or Release message.
int getIncreaseElapsedTime() const
Returns increased elapsed time.
Universe
defines option universe DHCPv4 or DHCPv6
Definition: option.h:82
Represents a DHCPv6 packet.
Definition: pkt6.h:44
bool checkMultiSubnet()
Check if multi subnet mode is enabled.
boost::shared_ptr< Element > ElementPtr
Definition: data.h:20
STL namespace.
Forward declaration to OptionInt.
const isc::dhcp::OptionCollection & getExtraOpts() const
Returns extra options to be inserted.
virtual dhcp::IfacePtr getIface()=0
See description of this method in PerfSocket class below.
void initPacketTemplates()
Reads packet templates from files.
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:24
bool includes(const Type lease_type) const
Checks if lease type implies request for the address, prefix (or both) as specified by the function a...
dhcp::Pkt4Ptr createRequestFromAck(const dhcp::Pkt4Ptr &ack)
Creates DHCPREQUEST from a DHCPACK message.
void setDefaults4(const dhcp::Pkt4Ptr &pkt)
Set default DHCPv4 packet parameters.
std::vector< uint8_t > TemplateBuffer
Packet template buffer.
Definition: test_control.h:123
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
structure that describes a single relay information
Definition: pkt6.h:85
PacketPtr getRandom()
Returns random packet from the storage.
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition: data.cc:1097
bool isRapidCommit() const
Check if rapid commit option used.
boost::shared_ptr< Option6IAPrefix > Option6IAPrefixPtr
Pointer to the Option6IAPrefix object.
DHCPv4 REQUEST-ACK (renewal)
bool isUseRelayedV6() const
Check if generated DHCPv6 messages should appear as relayed.
A generic exception that is thrown when an unexpected error condition occurs.
DHCPv6 RELEASE-REPLY.
std::map< uint8_t, dhcp::Pkt6Ptr > template_packets_v6_
Template for v6.
boost::shared_ptr< Pkt4 > Pkt4Ptr
A pointer to Pkt4 object.
Definition: pkt4.h:544
isc::asiolink::IOAddress addr_
Definition: socket_info.h:21
std::string getRandRelayAddr()
Returns random relay address.
Definition: dhcp6.h:26
uint8_t getIpVersion() const
Returns IP version.
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition: option.h:40
void sendDiscover4(const bool preload=false)
Send DHCPv4 DISCOVER message.
uint16_t ifindex_
Interface index.
Definition: perf_socket.h:29
int getServerIdOffset() const
Return server id offset in a packet.
boost::shared_ptr< isc::dhcp::Pkt > PktPtr
A pointer to either Pkt4 or Pkt6 packet.
Definition: pkt.h:797
uint32_t getSeed() const
Returns random seed.
PerfPkt4 (DHCPv4 packet)
Definition: perf_pkt4.h:42
The OutputBuffer class is a buffer abstraction for manipulating mutable data.
Definition: buffer.h:294
Ethernet 10Mbps.
Definition: dhcp4.h:56
std::vector< uint8_t > generateMacAddress(uint8_t &randomized)
Generate MAC address.
void sendRequest6(const dhcp::Pkt6Ptr &advertise_pkt6)
Send DHCPv6 REQUEST message.
Defines the logger used by the top-level component of kea-dhcp-ddns.
void sendRequest4(const dhcp::Pkt4Ptr &discover_pkt4, const dhcp::Pkt4Ptr &offer_pkt4)
Send DHCPv4 REQUEST message.
int getRandomOffset(const int arg_idx) const
Return randomization offset in a packet.
void copyIaOptions(const dhcp::Pkt6Ptr &pkt_from, dhcp::Pkt6Ptr &pkt_to)
Copies IA_NA or IA_PD option from one packet to another.
Represents DHCPv4 packet.
Definition: pkt4.h:37
static void handleChild(int sig)
Handle child signal.
uint8_t hop_count_
number of traversed relays (up to 32)
Definition: pkt6.h:95
PacketStorage< dhcp::Pkt6 > reply_storage_
Storage for reply messages.
dhcp::OptionPtr generateClientId(const dhcp::HWAddrPtr &hwaddr) const
Generate DHCPv4 client identifier from HW address.
DHCPv4 DISCOVER-OFFER.
std::string getServerName() const
Returns server name.
void saveFirstPacket(const dhcp::Pkt4Ptr &pkt)
Save the first DHCPv4 sent packet of the specified type.
bool isSeeded() const
Checks if seed provided.
bool sendRequestFromAck()
Send DHCPv4 renew (DHCPREQUEST).
DHCPv6 REQUEST-REPLY.
uint16_t getType() const
Returns option type (0-255 for DHCPv4, 0-65535 for DHCPv6)
Definition: option.h:288
CommandOptions & options_
Command options.
A generic exception that is thrown if a function is called in a prohibited way.
bool testDiags(const char diag)
Find if diagnostic flag has been set.
static const uint8_t HW_ETHER_LEN
Length of the Ethernet HW address (MAC) in bytes.
Definition: test_control.h:189
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
A generic exception that is thrown when an object can not be found.
isc::asiolink::IOAddress linkaddr_
fixed field in relay-forw/relay-reply
Definition: pkt6.h:96
dhcp::OptionBuffer first_packet_serverid_
Buffer holding server id received in first packet.
isc::asiolink::IOAddress peeraddr_
fixed field in relay-forw/relay-reply
Definition: pkt6.h:97
Socket wrapper structure.
Definition: perf_socket.h:26
int getTransactionIdOffset(const int arg_idx) const
Return transaction id offset in a packet.
dhcp::Pkt6Ptr createMessageFromReply(const uint16_t msg_type, const dhcp::Pkt6Ptr &reply)
Creates DHCPv6 message from the Reply packet.
Class that represents IAPREFIX option in DHCPv6.
boost::posix_time::time_period getTestPeriod() const
Get time period since the start of test.
ExchangeMode
2-way (cmd line param -i) or 4-way exchanges
std::string getWrapped() const
Returns wrapped command.
int getWaitForElapsedTime() const
Returns time to wait for elapsed time increase.
void setMacAddrGenerator(const NumberGeneratorPtr &generator)
Set new MAC address generator.
Definition: test_control.h:205
int getElapsedTimeOffset() const
Return elapsed time offset in a packet.
std::map< uint8_t, dhcp::Pkt4Ptr > template_packets_v4_
First packets send.
boost::shared_ptr< Option6IAAddr > Option6IAAddrPtr
A pointer to the isc::dhcp::Option6IAAddr object.
BasePerfSocket & socket_
Socket used for DHCP traffic.
void reset()
Resets internal state of the object.
static void handleInterrupt(int sig)
Handle interrupt signal.
Sequential numbers generator class.
Definition: test_control.h:157
void addExtraOpts(const dhcp::Pkt4Ptr &pkt4)
Inserts extra options specified by user.
uint8_t msg_type_
message type (RELAY-FORW oro RELAY-REPL)
Definition: pkt6.h:94
void passSentPacket(const ExchangeType xchg_type, const dhcp::PktPtr &packet)
Adds new packet to the sent packets list.
virtual bool send(const dhcp::Pkt4Ptr &pkt)=0
See description of this method in PerfSocket class below.