Kea  2.5.2
pkt6.cc
Go to the documentation of this file.
1 // Copyright (C) 2011-2022 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 
9 #include <dhcp/dhcp6.h>
10 #include <dhcp/libdhcp++.h>
11 #include <dhcp/option.h>
12 #include <dhcp/option_space.h>
14 #include <dhcp/option_vendor.h>
15 #include <dhcp/pkt6.h>
17 #include <util/io_utilities.h>
18 #include <exceptions/exceptions.h>
19 #include <dhcp/duid.h>
20 #include <dhcp/iface_mgr.h>
21 
22 #include <iterator>
23 #include <iostream>
24 #include <sstream>
25 
26 using namespace std;
27 using namespace isc::asiolink;
28 
31 
32 namespace isc {
33 namespace dhcp {
34 
35 Pkt6::RelayInfo::RelayInfo()
36  : msg_type_(0), hop_count_(0), linkaddr_(DEFAULT_ADDRESS6),
37  peeraddr_(DEFAULT_ADDRESS6), relay_msg_len_(0) {
38 }
39 
40 std::string Pkt6::RelayInfo::toText() const {
41  stringstream tmp;
42  tmp << "msg-type=" << static_cast<int>(msg_type_) << "(" << getName(msg_type_)
43  << "), hop-count=" << static_cast<int>(hop_count_) << "," << endl
44  << "link-address=" << linkaddr_.toText()
45  << ", peer-address=" << peeraddr_.toText() << ", "
46  << options_.size() << " option(s)" << endl;
47  for (const auto& option : options_) {
48  tmp << option.second->toText() << endl;
49  }
50  return (tmp.str());
51 }
52 
53 Pkt6::Pkt6(const uint8_t* buf, uint32_t buf_len, DHCPv6Proto proto /* = UDP */)
54  : Pkt(buf, buf_len, DEFAULT_ADDRESS6, DEFAULT_ADDRESS6, 0, 0), proto_(proto),
55  msg_type_(0) {
56 }
57 
58 Pkt6::Pkt6(uint8_t msg_type, uint32_t transid, DHCPv6Proto proto /*= UDP*/)
59  : Pkt(transid, DEFAULT_ADDRESS6, DEFAULT_ADDRESS6, 0, 0), proto_(proto),
60  msg_type_(msg_type) {
61 }
62 
63 size_t Pkt6::len() {
64  if (relay_info_.empty()) {
65  return (directLen());
66  } else {
67  // Unfortunately we need to re-calculate relay size every time, because
68  // we need to make sure that once a new option is added, its extra size
69  // is reflected in Pkt6::len().
71  return (relay_info_[0].relay_msg_len_ + getRelayOverhead(relay_info_[0]));
72  }
73 }
74 
75 void
76 Pkt6::prepareGetAnyRelayOption(const RelaySearchOrder& order,
77  int& start, int& end, int& direction) const {
78  switch (order) {
80  // Search backwards
81  start = relay_info_.size() - 1;
82  end = 0;
83  direction = -1;
84  break;
86  // Search forward
87  start = 0;
88  end = relay_info_.size() - 1;
89  direction = 1;
90  break;
91  case RELAY_GET_FIRST:
92  // Look at the innermost relay only
93  start = relay_info_.size() - 1;
94  end = start;
95  direction = 1;
96  break;
97  case RELAY_GET_LAST:
98  // Look at the outermost relay only
99  start = 0;
100  end = 0;
101  direction = 1;
102  }
103 }
104 
105 OptionPtr
106 Pkt6::getNonCopiedAnyRelayOption(const uint16_t option_code,
107  const RelaySearchOrder& order) const {
108  if (relay_info_.empty()) {
109  // There's no relay info, this is a direct message
110  return (OptionPtr());
111  }
112 
113  int start = 0; // First relay to check
114  int end = 0; // Last relay to check
115  int direction = 0; // How we going to iterate: forward or backward?
116 
117  prepareGetAnyRelayOption(order, start, end, direction);
118 
119  // This is a tricky loop. It must go from start to end, but it must work in
120  // both directions (start > end; or start < end). We can't use regular
121  // exit condition, because we don't know whether to use i <= end or i >= end.
122  // That's why we check if in the next iteration we would go past the
123  // list (end + direction). It is similar to STL concept of end pointing
124  // to a place after the last element
125  for (int i = start; i != end + direction; i += direction) {
126  OptionPtr opt = getNonCopiedRelayOption(option_code, i);
127  if (opt) {
128  return (opt);
129  }
130  }
131 
132  // We iterated over specified relays and haven't found what we were
133  // looking for
134  return (OptionPtr());
135 }
136 
137 OptionPtr
138 Pkt6::getAnyRelayOption(const uint16_t option_code,
139  const RelaySearchOrder& order) {
140 
141  if (relay_info_.empty()) {
142  // There's no relay info, this is a direct message
143  return (OptionPtr());
144  }
145 
146  int start = 0; // First relay to check
147  int end = 0; // Last relay to check
148  int direction = 0; // How we going to iterate: forward or backward?
149 
150  prepareGetAnyRelayOption(order, start, end, direction);
151 
152  // This is a tricky loop. It must go from start to end, but it must work in
153  // both directions (start > end; or start < end). We can't use regular
154  // exit condition, because we don't know whether to use i <= end or i >= end.
155  // That's why we check if in the next iteration we would go past the
156  // list (end + direction). It is similar to STL concept of end pointing
157  // to a place after the last element
158  for (int i = start; i != end + direction; i += direction) {
159  OptionPtr opt = getRelayOption(option_code, i);
160  if (opt) {
161  return (opt);
162  }
163  }
164 
165  // We iterated over specified relays and haven't found what we were
166  // looking for
167  return (OptionPtr());
168 }
169 
171 Pkt6::getNonCopiedAllRelayOptions(const uint16_t option_code,
172  const RelaySearchOrder& order) const {
173  if (relay_info_.empty()) {
174  // There's no relay info, this is a direct message
175  return (OptionCollection());
176  }
177 
178  int start = 0; // First relay to check
179  int end = 0; // Last relay to check
180  int direction = 0; // How we going to iterate: forward or backward?
181 
182  prepareGetAnyRelayOption(order, start, end, direction);
183 
184  // This is a tricky loop. It must go from start to end, but it must work in
185  // both directions (start > end; or start < end). We can't use regular
186  // exit condition, because we don't know whether to use i <= end or i >= end.
187  // That's why we check if in the next iteration we would go past the
188  // list (end + direction). It is similar to STL concept of end pointing
189  // to a place after the last element
190  OptionCollection opts;
191  for (int i = start; i != end + direction; i += direction) {
192  std::pair<OptionCollection::const_iterator,
193  OptionCollection::const_iterator> range =
194  relay_info_[i].options_.equal_range(option_code);
195  opts.insert(range.first, range.second);
196  }
197  return (opts);
198 }
199 
201 Pkt6::getAllRelayOptions(const uint16_t option_code,
202  const RelaySearchOrder& order) {
203 
204  if (relay_info_.empty()) {
205  // There's no relay info, this is a direct message
206  return (OptionCollection());
207  }
208 
209  int start = 0; // First relay to check
210  int end = 0; // Last relay to check
211  int direction = 0; // How we going to iterate: forward or backward?
212 
213  prepareGetAnyRelayOption(order, start, end, direction);
214 
215  // This is a tricky loop. It must go from start to end, but it must work in
216  // both directions (start > end; or start < end). We can't use regular
217  // exit condition, because we don't know whether to use i <= end or i >= end.
218  // That's why we check if in the next iteration we would go past the
219  // list (end + direction). It is similar to STL concept of end pointing
220  // to a place after the last element
221  OptionCollection opts;
222  for (int i = start; i != end + direction; i += direction) {
223  std::pair<OptionCollection::iterator,
224  OptionCollection::iterator> range =
225  relay_info_[i].options_.equal_range(option_code);
226  // If options should be copied on retrieval, we should now iterate over
227  // matching options, copy them and replace the original ones with new
228  // instances.
230  for (OptionCollection::iterator opt_it = range.first;
231  opt_it != range.second; ++opt_it) {
232  OptionPtr option_copy = opt_it->second->clone();
233  opt_it->second = option_copy;
234  }
235  }
236  opts.insert(range.first, range.second);
237  }
238  return (opts);
239 }
240 
241 OptionPtr
242 Pkt6::getNonCopiedRelayOption(const uint16_t opt_type,
243  const uint8_t relay_level) const {
244  if (relay_level >= relay_info_.size()) {
245  isc_throw(OutOfRange, "This message was relayed "
246  << relay_info_.size() << " time(s)."
247  << " There is no info about "
248  << relay_level + 1 << " relay.");
249  }
250 
251  OptionCollection::const_iterator x = relay_info_[relay_level].options_.find(opt_type);
252  if (x != relay_info_[relay_level].options_.end()) {
253  return (x->second);
254  }
255 
256  return (OptionPtr());
257 }
258 
259 OptionPtr
260 Pkt6::getRelayOption(const uint16_t opt_type, const uint8_t relay_level) {
261  if (relay_level >= relay_info_.size()) {
262  isc_throw(OutOfRange, "This message was relayed "
263  << relay_info_.size() << " time(s)."
264  << " There is no info about "
265  << relay_level + 1 << " relay.");
266  }
267 
268  OptionCollection::iterator x = relay_info_[relay_level].options_.find(opt_type);
269  if (x != relay_info_[relay_level].options_.end()) {
271  OptionPtr relay_option_copy = x->second->clone();
272  x->second = relay_option_copy;
273  }
274  return (x->second);
275  }
276 
277  return (OptionPtr());
278 }
279 
281 Pkt6::getNonCopiedRelayOptions(const uint16_t opt_type,
282  const uint8_t relay_level) const {
283  if (relay_level >= relay_info_.size()) {
284  isc_throw(OutOfRange, "This message was relayed "
285  << relay_info_.size() << " time(s)."
286  << " There is no info about "
287  << relay_level + 1 << " relay.");
288  }
289 
290  std::pair<OptionCollection::const_iterator,
291  OptionCollection::const_iterator> range =
292  relay_info_[relay_level].options_.equal_range(opt_type);
293  return (OptionCollection(range.first, range.second));
294 }
295 
297 Pkt6::getRelayOptions(const uint16_t opt_type,
298  const uint8_t relay_level) {
299  if (relay_level >= relay_info_.size()) {
300  isc_throw(OutOfRange, "This message was relayed "
301  << relay_info_.size() << " time(s)."
302  << " There is no info about "
303  << relay_level + 1 << " relay.");
304  }
305 
306  OptionCollection options_copy;
307 
308  std::pair<OptionCollection::iterator,
309  OptionCollection::iterator> range =
310  relay_info_[relay_level].options_.equal_range(opt_type);
311  // If options should be copied on retrieval, we should now iterate over
312  // matching options, copy them and replace the original ones with new
313  // instances.
315  for (OptionCollection::iterator opt_it = range.first;
316  opt_it != range.second; ++opt_it) {
317  OptionPtr option_copy = opt_it->second->clone();
318  opt_it->second = option_copy;
319  }
320  }
321  // Finally, return updated options. This can also be empty in some cases.
322  return (OptionCollection(range.first, range.second));
323 }
324 
326 Pkt6::getRelay6LinkAddress(uint8_t relay_level) const {
327  if (relay_level >= relay_info_.size()) {
328  isc_throw(OutOfRange, "This message was relayed " << relay_info_.size() << " time(s)."
329  << " There is no info about " << relay_level + 1 << " relay.");
330  }
331 
332  return (relay_info_[relay_level].linkaddr_);
333 }
334 
336 Pkt6::getRelay6PeerAddress(uint8_t relay_level) const {
337  if (relay_level >= relay_info_.size()) {
338  isc_throw(OutOfRange, "This message was relayed " << relay_info_.size() << " time(s)."
339  << " There is no info about " << relay_level + 1 << " relay.");
340  }
341 
342  return (relay_info_[relay_level].peeraddr_);
343 }
344 
345 uint16_t Pkt6::getRelayOverhead(const RelayInfo& relay) const {
346  uint16_t len = DHCPV6_RELAY_HDR_LEN // fixed header
347  + Option::OPTION6_HDR_LEN; // header of the relay-msg option
348 
349  for (const auto& opt : relay.options_) {
350  len += (opt.second)->len();
351  }
352 
353  return (len);
354 }
355 
357 
358  uint16_t len = directLen(); // start with length of all options
359 
360  for (int relay_index = relay_info_.size(); relay_index > 0; --relay_index) {
361  relay_info_[relay_index - 1].relay_msg_len_ = len;
362  len += getRelayOverhead(relay_info_[relay_index - 1]);
363  }
364 
365  return (len);
366 }
367 
368 uint16_t Pkt6::directLen() const {
369  uint16_t length = DHCPV6_PKT_HDR_LEN; // DHCPv6 header
370 
371  for (const auto& it : options_) {
372  length += it.second->len();
373  }
374 
375  return (length);
376 }
377 
378 
379 void
381  switch (proto_) {
382  case UDP:
383  packUDP();
384  break;
385  case TCP:
386  packTCP();
387  break;
388  default:
389  isc_throw(BadValue, "Invalid protocol specified (non-TCP, non-UDP)");
390  }
391 }
392 
393 void
395  try {
396  // Make sure that the buffer is empty before we start writing to it.
397  buffer_out_.clear();
398 
399  // is this a relayed packet?
400  if (!relay_info_.empty()) {
401 
402  // calculate size needed for each relay (if there is only one relay,
403  // then it will be equal to "regular" length + relay-forw header +
404  // size of relay-msg option header + possibly size of interface-id
405  // option (if present). If there is more than one relay, the whole
406  // process is called iteratively for each relay.
408 
409  // Now for each relay, we need to...
410  for (vector<RelayInfo>::iterator relay = relay_info_.begin();
411  relay != relay_info_.end(); ++relay) {
412 
413  // build relay-forw/relay-repl header (see RFC 8415, section 9)
414  buffer_out_.writeUint8(relay->msg_type_);
415  buffer_out_.writeUint8(relay->hop_count_);
416  buffer_out_.writeData(&(relay->linkaddr_.toBytes()[0]),
417  isc::asiolink::V6ADDRESS_LEN);
418  buffer_out_.writeData(&relay->peeraddr_.toBytes()[0],
419  isc::asiolink::V6ADDRESS_LEN);
420 
421  // store every option in this relay scope. Usually that will be
422  // only interface-id, but occasionally other options may be
423  // present here as well (vendor-opts for Cable modems,
424  // subscriber-id, remote-id, options echoed back from Echo
425  // Request Option, etc.)
426  for (const auto& opt : relay->options_) {
427  (opt.second)->pack(buffer_out_);
428  }
429 
430  // and include header relay-msg option. Its payload will be
431  // generated in the next iteration (if there are more relays)
432  // or outside the loop (if there are no more relays and the
433  // payload is a direct message)
435  buffer_out_.writeUint16(relay->relay_msg_len_);
436  }
437 
438  }
439 
440  // DHCPv6 header: message-type (1 octet) + transaction id (3 octets)
442  // store 3-octet transaction-id
443  buffer_out_.writeUint8( (transid_ >> 16) & 0xff );
444  buffer_out_.writeUint8( (transid_ >> 8) & 0xff );
445  buffer_out_.writeUint8( (transid_) & 0xff );
446 
447  // the rest are options
449  }
450  catch (const Exception& e) {
451  // An exception is thrown and message will be written to Logger
453  }
454 }
455 
456 void
459  isc_throw(NotImplemented, "DHCPv6 over TCP (bulk leasequery and failover)"
460  " not implemented yet.");
461 }
462 
463 void
465  switch (proto_) {
466  case UDP:
467  return unpackUDP();
468  case TCP:
469  return unpackTCP();
470  default:
471  isc_throw(BadValue, "Invalid protocol specified (non-TCP, non-UDP)");
472  }
473 }
474 
475 void
477  if (data_.size() < 4) {
478  isc_throw(BadValue, "Received truncated UDP DHCPv6 packet of size "
479  << data_.size() << ", DHCPv6 header alone has 4 bytes.");
480  }
481  msg_type_ = data_[0];
482  switch (msg_type_) {
483  case DHCPV6_SOLICIT:
484  case DHCPV6_ADVERTISE:
485  case DHCPV6_REQUEST:
486  case DHCPV6_CONFIRM:
487  case DHCPV6_RENEW:
488  case DHCPV6_REBIND:
489  case DHCPV6_REPLY:
490  case DHCPV6_DECLINE:
491  case DHCPV6_RECONFIGURE:
493  case DHCPV6_DHCPV4_QUERY:
495  default: // assume that unknown messages are not using relay format
496  {
497  return (unpackMsg(data_.begin(), data_.end()));
498  }
499  case DHCPV6_RELAY_FORW:
500  case DHCPV6_RELAY_REPL:
501  return (unpackRelayMsg());
502  }
503 }
504 
505 void
506 Pkt6::unpackMsg(OptionBuffer::const_iterator begin,
507  OptionBuffer::const_iterator end) {
508  size_t size = std::distance(begin, end);
509  if (size < 4) {
510  // truncated message (less than 4 bytes)
511  isc_throw(BadValue, "Received truncated UDP DHCPv6 packet of size "
512  << data_.size() << ", DHCPv6 header alone has 4 bytes.");
513  }
514 
515  msg_type_ = *begin++;
516 
517  transid_ = ( (*begin++) << 16 ) +
518  ((*begin++) << 8) + (*begin++);
519  transid_ = transid_ & 0xffffff;
520 
521  // See below about invoking Postel's law, as we aren't using
522  // size we don't need to update it. If we do so in the future
523  // perhaps for stats gathering we can uncomment this.
524  // size -= sizeof(uint32_t); // We just parsed 4 bytes header
525 
526  OptionBuffer opt_buffer(begin, end);
527 
528  // If custom option parsing function has been set, use this function
529  // to parse options. Otherwise, use standard function from libdhcp.
530  size_t offset = LibDHCP::unpackOptions6(opt_buffer, DHCP6_OPTION_SPACE, options_);
531 
532  // If offset is not equal to the size, then something is wrong here. We
533  // either parsed past input buffer (bug in our code) or we haven't parsed
534  // everything (received trailing garbage or truncated option).
535  //
536  // Invoking Jon Postel's law here: be conservative in what you send, and be
537  // liberal in what you accept. There's no easy way to log something from
538  // libdhcp++ library, so we just choose to be silent about remaining
539  // bytes. We also need to quell compiler warning about unused offset
540  // variable.
541  //
542  // if (offset != size) {
543  // isc_throw(BadValue, "Received DHCPv6 buffer of size " << size
544  // << ", were able to parse " << offset << " bytes.");
545  // }
546  (void)offset;
547 }
548 
549 void
551 
552  // we use offset + bufsize, because we want to avoid creating unnecessary
553  // copies. There may be up to 32 relays. While using InputBuffer would
554  // be probably a bit cleaner, copying data up to 32 times is unacceptable
555  // price here. Hence a single buffer with offsets and lengths.
556  size_t bufsize = data_.size();
557  size_t offset = 0;
558 
559  while (bufsize >= DHCPV6_RELAY_HDR_LEN) {
560 
561  RelayInfo relay;
562 
563  size_t relay_msg_offset = 0;
564  size_t relay_msg_len = 0;
565 
566  // parse fixed header first (first 34 bytes)
567  relay.msg_type_ = data_[offset++];
568  relay.hop_count_ = data_[offset++];
569  relay.linkaddr_ = IOAddress::fromBytes(AF_INET6, &data_[offset]);
570  offset += isc::asiolink::V6ADDRESS_LEN;
571  relay.peeraddr_ = IOAddress::fromBytes(AF_INET6, &data_[offset]);
572  offset += isc::asiolink::V6ADDRESS_LEN;
573  bufsize -= DHCPV6_RELAY_HDR_LEN; // 34 bytes (1+1+16+16)
574 
575  // parse the rest as options
576  OptionBuffer opt_buffer(&data_[offset], &data_[offset] + bufsize);
577 
578  // If custom option parsing function has been set, use this function
579  // to parse options. Otherwise, use standard function from libdhcp.
581  &relay_msg_offset, &relay_msg_len);
582 
584  //relay.interface_id_ = options->getOption(D6O_INTERFACE_ID);
585  //relay.subscriber_id_ = options->getOption(D6O_SUBSCRIBER_ID);
586  //relay.remote_id_ = options->getOption(D6O_REMOTE_ID);
587 
588  if (relay_msg_offset == 0 || relay_msg_len == 0) {
589  isc_throw(BadValue, "Mandatory relay-msg option missing");
590  }
591 
592  // store relay information parsed so far
593  addRelayInfo(relay);
594 
596 
597  if (relay_msg_len >= bufsize) {
598  // length of the relay_msg option extends beyond end of the message
599  isc_throw(Unexpected, "Relay-msg option is truncated.");
600  }
601  uint8_t inner_type = data_[offset + relay_msg_offset];
602  offset += relay_msg_offset; // offset is relative
603  bufsize = relay_msg_len; // length is absolute
604 
605  if ( (inner_type != DHCPV6_RELAY_FORW) &&
606  (inner_type != DHCPV6_RELAY_REPL)) {
607  // Ok, the inner message is not encapsulated, let's decode it
608  // directly
609  return (unpackMsg(data_.begin() + offset, data_.begin() + offset
610  + relay_msg_len));
611  }
612 
613  // Oh well, there's inner relay-forw or relay-repl inside. Let's
614  // unpack it as well. The next loop iteration will take care
615  // of that.
616  }
617 
618  if ( (offset == data_.size()) && (bufsize == 0) ) {
619  // message has been parsed completely
620  return;
621  }
622 
624 }
625 
626 void
628  if (relay_info_.size() > HOP_COUNT_LIMIT) {
629  isc_throw(BadValue, "Massage cannot be encapsulated more than 32 times");
630  }
631 
633  relay_info_.push_back(relay);
634 }
635 
636 void
638  isc_throw(Unexpected, "DHCPv6 over TCP (bulk leasequery and failover) "
639  "not implemented yet.");
640 }
641 
642 HWAddrPtr
644  HWAddrPtr mac;
646  if (!opt_duid) {
647  return (mac);
648  }
649 
650  uint8_t hlen = opt_duid->getData().size();
651  if (!hlen) {
652  return (mac);
653  }
654  vector<uint8_t> hw_addr(hlen, 0);
655  std::vector<unsigned char> duid_data = opt_duid->getData();
656 
657  // Read the first two bytes. That duid type.
658  uint16_t duid_type = util::readUint16(&duid_data[0], duid_data.size());
659 
660  switch (duid_type) {
661  case DUID::DUID_LL:
662  {
663  // 2 bytes of duid type, 2 bytes of hardware type and at least
664  // 1 byte of actual identification
665  if (duid_data.size() >= 5) {
666  uint16_t hwtype = util::readUint16(&duid_data[2],
667  duid_data.size() - 2);
668  mac.reset(new HWAddr(&duid_data[4], duid_data.size() - 4, hwtype));
669  }
670  break;
671  }
672  case DUID::DUID_LLT:
673  {
674  // 2 bytes of duid type, 2 bytes of hardware, 4 bytes for timestamp,
675  // and at least 1 byte of actual identification
676  if (duid_data.size() >= 9) {
677  uint16_t hwtype = util::readUint16(&duid_data[2],
678  duid_data.size() - 2);
679  mac.reset(new HWAddr(&duid_data[8], duid_data.size() - 8, hwtype));
680  }
681  break;
682  }
683  default:
684  break;
685  }
686 
687  if (mac) {
688  mac->source_ = HWAddr::HWADDR_SOURCE_DUID;
689  }
690 
691  return (mac);
692 }
693 
694 std::string
695 Pkt6::makeLabel(const DuidPtr duid, const uint32_t transid,
696  const HWAddrPtr& hwaddr) {
697  // Create label with DUID and HW address.
698  std::stringstream label;
699  label << makeLabel(duid, hwaddr);
700 
701  // Append transaction id.
702  label << ", tid=0x" << std::hex << transid << std::dec;
703 
704  return (label.str());
705 }
706 
707 std::string
708 Pkt6::makeLabel(const DuidPtr duid, const HWAddrPtr& hwaddr) {
709  std::stringstream label;
710  // DUID should be present at all times, so explicitly inform when
711  // it is no present (no info).
712  label << "duid=[" << (duid ? duid->toText() : "no info")
713  << "]";
714 
715  // HW address is typically not carried in the DHCPv6 messages
716  // and can be extracted using various, but not fully reliable,
717  // techniques. If it is not present, don't print anything.
718  if (hwaddr) {
719  label << ", [" << hwaddr->toText() << "]";
720  }
721 
722  return (label.str());
723 }
724 
725 std::string
726 Pkt6::getLabel() const {
731  return (makeLabel(getClientId(), getTransid(), HWAddrPtr()));}
732 
733 std::string
734 Pkt6::toText() const {
735  stringstream tmp;
736 
737  // First print the basics
738  tmp << "localAddr=[" << local_addr_ << "]:" << local_port_
739  << " remoteAddr=[" << remote_addr_ << "]:" << remote_port_ << endl;
740  tmp << "msgtype=" << static_cast<int>(msg_type_) << "(" << getName(msg_type_)
741  << "), transid=0x" <<
742  hex << transid_ << dec << endl;
743 
744  // Then print the options
745  for (const auto& opt : options_) {
746  tmp << opt.second->toText() << std::endl;
747  }
748 
749  // Finally, print the relay information (if present)
750  if (!relay_info_.empty()) {
751  tmp << relay_info_.size() << " relay(s):" << endl;
752  int cnt = 0;
753  for (const auto& relay : relay_info_) {
754  tmp << "relay[" << cnt++ << "]: " << relay.toText();
755  }
756  } else {
757  tmp << "No relays traversed." << endl;
758  }
759  return tmp.str();
760 }
761 
762 DuidPtr
765  try {
766  // This will throw if the DUID length is larger than 128 bytes
767  // or is too short.
768  return (opt_duid ? DuidPtr(new DUID(opt_duid->getData())) : DuidPtr());
769  } catch (...) {
770  // Do nothing. This method is used only by getLabel(), which is
771  // used for logging purposes. We should not throw, but rather
772  // report no DUID. We should not log anything, as we're in the
773  // process of logging something for this packet. So the only
774  // choice left is to return an empty pointer.
775  }
776  return (DuidPtr());
777 }
778 
779 const char*
780 Pkt6::getName(const uint8_t type) {
781  static const char* ADVERTISE = "ADVERTISE";
782  static const char* CONFIRM = "CONFIRM";
783  static const char* DECLINE = "DECLINE";
784  static const char* INFORMATION_REQUEST = "INFORMATION_REQUEST";
785  static const char* LEASEQUERY = "LEASEQUERY";
786  static const char* LEASEQUERY_DATA = "LEASEQUERY_DATA";
787  static const char* LEASEQUERY_DONE = "LEASEQUERY_DONE";
788  static const char* LEASEQUERY_REPLY = "LEASEQUERY_REPLY";
789  static const char* REBIND = "REBIND";
790  static const char* RECONFIGURE = "RECONFIGURE";
791  static const char* RELAY_FORW = "RELAY_FORWARD";
792  static const char* RELAY_REPL = "RELAY_REPLY";
793  static const char* RELEASE = "RELEASE";
794  static const char* RENEW = "RENEW";
795  static const char* REPLY = "REPLY";
796  static const char* REQUEST = "REQUEST";
797  static const char* SOLICIT = "SOLICIT";
798  static const char* DHCPV4_QUERY = "DHCPV4_QUERY";
799  static const char* DHCPV4_RESPONSE = "DHCPV4_RESPONSE";
800  static const char* UNKNOWN = "UNKNOWN";
801 
802  switch (type) {
803  case DHCPV6_ADVERTISE:
804  return (ADVERTISE);
805 
806  case DHCPV6_CONFIRM:
807  return (CONFIRM);
808 
809  case DHCPV6_DECLINE:
810  return (DECLINE);
811 
813  return (INFORMATION_REQUEST);
814 
815  case DHCPV6_LEASEQUERY:
816  return (LEASEQUERY);
817 
819  return (LEASEQUERY_DATA);
820 
822  return (LEASEQUERY_DONE);
823 
825  return (LEASEQUERY_REPLY);
826 
827  case DHCPV6_REBIND:
828  return (REBIND);
829 
830  case DHCPV6_RECONFIGURE:
831  return (RECONFIGURE);
832 
833  case DHCPV6_RELAY_FORW:
834  return (RELAY_FORW);
835 
836  case DHCPV6_RELAY_REPL:
837  return (RELAY_REPL);
838 
839  case DHCPV6_RELEASE:
840  return (RELEASE);
841 
842  case DHCPV6_RENEW:
843  return (RENEW);
844 
845  case DHCPV6_REPLY:
846  return (REPLY);
847 
848  case DHCPV6_REQUEST:
849  return (REQUEST);
850 
851  case DHCPV6_SOLICIT:
852  return (SOLICIT);
853 
854  case DHCPV6_DHCPV4_QUERY:
855  return (DHCPV4_QUERY);
856 
858  return (DHCPV4_RESPONSE);
859 
860  default:
861  ;
862  }
863  return (UNKNOWN);
864 }
865 
866 const char* Pkt6::getName() const {
867  return (getName(getType()));
868 }
869 
870 void Pkt6::copyRelayInfo(const Pkt6Ptr& question) {
871 
872  // We use index rather than iterator, because we need that as a parameter
873  // passed to getNonCopiedRelayOption()
874  for (size_t i = 0; i < question->relay_info_.size(); ++i) {
875  RelayInfo info;
876  info.msg_type_ = DHCPV6_RELAY_REPL;
877  info.hop_count_ = question->relay_info_[i].hop_count_;
878  info.linkaddr_ = question->relay_info_[i].linkaddr_;
879  info.peeraddr_ = question->relay_info_[i].peeraddr_;
880 
881  // Is there an interface-id option in this nesting level?
882  // If there is, we need to echo it back
883  OptionPtr opt = question->getNonCopiedRelayOption(D6O_INTERFACE_ID, i);
884  // taken from question->RelayInfo_[i].options_
885  if (opt) {
886  info.options_.insert(make_pair(opt->getType(), opt));
887  }
888 
889  // Same for relay-source-port option
890  opt = question->getNonCopiedRelayOption(D6O_RELAY_SOURCE_PORT, i);
891  if (opt) {
892  info.options_.insert(make_pair(opt->getType(), opt));
893  }
894 
896 
897  // Add this relay-forw info (client's message) to our relay-repl
898  // message (server's response)
899  relay_info_.push_back(info);
900  }
901 }
902 
903 HWAddrPtr
905  if (relay_info_.empty()) {
906  // This is a direct message, use source address
907  return (getMACFromIPv6(remote_addr_));
908  }
909 
910  // This is a relayed message, get the peer-addr from the first relay-forw
911  return (getMACFromIPv6(relay_info_[relay_info_.size() - 1].peeraddr_));
912 }
913 
914 HWAddrPtr
916  HWAddrPtr mac;
917 
918  // This is not a direct message
919  if (!relay_info_.empty()) {
920  // RFC6969 Section 6: Look for the client_linklayer_addr option on the
921  // relay agent closest to the client
924  if (opt) {
925  const OptionBuffer data = opt->getData();
926  // This client link address option is supposed to be
927  // 2 bytes of link-layer type followed by link-layer address.
928  if (data.size() >= 3) {
929  // +2, -2 means to skip the initial 2 bytes which are
930  // hwaddress type
931  mac.reset(new HWAddr(&data[0] + 2, data.size() - 2,
932  opt->getUint16()));
933 
935  }
936  }
937  }
938 
939  return mac;
940 }
941 
942 HWAddrPtr
944  HWAddrPtr mac;
945  OptionVendorPtr vendor;
946  for (auto opt : getNonCopiedOptions(D6O_VENDOR_OPTS)) {
947  if (opt.first != D6O_VENDOR_OPTS) {
948  continue;
949  }
950  vendor = boost::dynamic_pointer_cast< OptionVendor>(opt.second);
951  // Check if this is indeed DOCSIS3 environment
952  if (!vendor || vendor->getVendorId() != VENDOR_ID_CABLE_LABS) {
953  continue;
954  }
955  // If it is, try to get device-id option
956  OptionPtr device_id = vendor->getOption(DOCSIS3_V6_DEVICE_ID);
957  if (device_id) {
958  // If the option contains any data, use it as MAC address
959  if (!device_id->getData().empty()) {
960  mac.reset(new HWAddr(device_id->getData(), HTYPE_DOCSIS));
961  mac->source_ = HWAddr::HWADDR_SOURCE_DOCSIS_MODEM;
962  break;
963  }
964  }
965  }
966 
967  return mac;
968 }
969 
970 HWAddrPtr
972  if (relay_info_.empty()) {
973  return (HWAddrPtr());
974  }
975 
976  // If the message passed through a CMTS, there'll
977  // CMTS-specific options in it.
978  HWAddrPtr mac;
979  OptionVendorPtr vendor;
980  for (auto opt : getAllRelayOptions(D6O_VENDOR_OPTS,
982  if (opt.first != D6O_VENDOR_OPTS) {
983  continue;
984  }
985  vendor = boost::dynamic_pointer_cast< OptionVendor>(opt.second);
986  // Check if this is indeed DOCSIS3 environment
987  if (!vendor || vendor->getVendorId() != VENDOR_ID_CABLE_LABS) {
988  continue;
989  }
990  // Try to get cable modem mac
991  OptionPtr cm_mac = vendor->getOption(DOCSIS3_V6_CMTS_CM_MAC);
992 
993  // If the option contains any data, use it as MAC address
994  if (cm_mac && !cm_mac->getData().empty()) {
995  mac.reset(new HWAddr(cm_mac->getData(), HTYPE_DOCSIS));
996  mac->source_ = HWAddr::HWADDR_SOURCE_DOCSIS_CMTS;
997  break;
998  }
999  }
1000 
1001  return (mac);
1002 }
1003 
1004 HWAddrPtr
1006  HWAddrPtr mac;
1007 
1008  // If this is relayed message
1009  if (!relay_info_.empty()) {
1010  // Get remote-id option from a relay agent closest to the client
1012  if (opt) {
1013  const OptionBuffer data = opt->getData();
1014  // This remote-id option is supposed to be 4 bytes of
1015  // of enterprise-number followed by remote-id.
1016  if (data.size() >= 5) {
1017  // Let's get the interface this packet was received on.
1018  // We need it to get the hardware type.
1020  uint16_t hwtype = 0; // not specified
1021 
1022  // If we get the interface HW type, great! If not,
1023  // let's not panic.
1024  if (iface) {
1025  hwtype = iface->getHWType();
1026  }
1027 
1028  size_t len = data.size() - 4;
1029 
1030  if (len > HWAddr::MAX_HWADDR_LEN) {
1032  }
1033 
1034  // Skip the initial 4 bytes which are enterprise-number.
1035  mac.reset(new HWAddr(&data[0] + 4, len, hwtype));
1036  mac->source_ = HWAddr::HWADDR_SOURCE_REMOTE_ID;
1037  }
1038  }
1039  }
1040 
1041  return (mac);
1042 }
1043 
1044 } // end of namespace isc::dhcp
1045 } // end of namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown if a function is called in a prohibited way.
A generic exception that is thrown when a function is not implemented.
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 unexpected error condition occurs.
Holds DUID (DHCPv6 Unique Identifier)
Definition: duid.h:138
@ DUID_LL
link-layer, see RFC3315, section 11.4
Definition: duid.h:158
@ DUID_LLT
link-layer + time, see RFC3315, section 11.2
Definition: duid.h:156
IfacePtr getIface(const unsigned int ifindex)
Returns interface specified interface index.
Definition: iface_mgr.cc:903
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition: iface_mgr.cc:53
static size_t unpackOptions6(const OptionBuffer &buf, const std::string &option_space, isc::dhcp::OptionCollection &options, size_t *relay_msg_offset=0, size_t *relay_msg_len=0)
Parses provided buffer as DHCPv6 options and creates Option objects.
Definition: libdhcp++.cc:309
static void packOptions6(isc::util::OutputBuffer &buf, const isc::dhcp::OptionCollection &options)
Stores DHCPv6 options in a buffer.
Definition: libdhcp++.cc:1236
static const size_t OPTION6_HDR_LEN
length of any DHCPv6 option header
Definition: option.h:80
virtual std::string toText() const
Returns text representation of the packet.
Definition: pkt6.cc:734
uint16_t calculateRelaySizes()
Calculates overhead for all relays defined for this message.
Definition: pkt6.cc:356
virtual HWAddrPtr getMACFromIPv6RelayOpt()
Extract MAC/Hardware address from client link-layer address.
Definition: pkt6.cc:915
DHCPv6Proto
DHCPv6 transport protocol.
Definition: pkt6.h:53
virtual std::string getLabel() const
Returns text representation of the primary packet identifiers.
Definition: pkt6.cc:726
OptionCollection getRelayOptions(uint16_t option_code, uint8_t nesting_level)
Returns options inserted by relay.
Definition: pkt6.cc:297
static const size_t DHCPV6_PKT_HDR_LEN
specifies non-relayed DHCPv6 packet header length (over UDP)
Definition: pkt6.h:47
virtual size_t len()
Returns length of the packet.
Definition: pkt6.cc:63
virtual void pack()
Prepares on-wire format.
Definition: pkt6.cc:380
OptionPtr getRelayOption(uint16_t option_code, uint8_t nesting_level)
Returns option inserted by relay.
Definition: pkt6.cc:260
virtual HWAddrPtr getMACFromRemoteIdRelayOption()
Attempts to obtain MAC address from remote-id relay option.
Definition: pkt6.cc:1005
void unpackTCP()
Parses on-wire form of TCP DHCPv6 packet.
Definition: pkt6.cc:637
const char * getName() const
Returns name of the DHCPv6 message.
Definition: pkt6.cc:866
virtual HWAddrPtr getMACFromSrcLinkLocalAddr()
Attempts to generate MAC/Hardware address from IPv6 link-local address.
Definition: pkt6.cc:904
OptionPtr getNonCopiedRelayOption(const uint16_t opt_type, const uint8_t relay_level) const
Returns pointer to an option inserted by relay agent.
Definition: pkt6.cc:242
void unpackRelayMsg()
Unpacks relayed message (RELAY-FORW or RELAY-REPL).
Definition: pkt6.cc:550
RelaySearchOrder
defines relay search pattern
Definition: pkt6.h:74
@ RELAY_GET_LAST
Definition: pkt6.h:78
@ RELAY_SEARCH_FROM_CLIENT
Definition: pkt6.h:75
@ RELAY_GET_FIRST
Definition: pkt6.h:77
@ RELAY_SEARCH_FROM_SERVER
Definition: pkt6.h:76
static std::string makeLabel(const DuidPtr duid, const uint32_t transid, const HWAddrPtr &hwaddr)
Returns text representation of the given packet identifiers.
Definition: pkt6.cc:695
OptionPtr getAnyRelayOption(const uint16_t option_code, const RelaySearchOrder &order)
Return first instance of a specified option.
Definition: pkt6.cc:138
DuidPtr getClientId() const
Retrieves the DUID from the Client Identifier option.
Definition: pkt6.cc:763
void packTCP()
Builds on wire packet for TCP transmission.
Definition: pkt6.cc:457
OptionPtr getNonCopiedAnyRelayOption(const uint16_t option_code, const RelaySearchOrder &order) const
Returns pointer to an instance of specified option.
Definition: pkt6.cc:106
void copyRelayInfo(const Pkt6Ptr &question)
copies relay information from client's packet to server's response
Definition: pkt6.cc:870
DHCPv6Proto proto_
UDP (usually) or TCP (bulk leasequery or failover)
Definition: pkt6.h:617
const isc::asiolink::IOAddress & getRelay6PeerAddress(uint8_t relay_level) const
return the peer address field from a relay option
Definition: pkt6.cc:336
virtual void unpack()
Dispatch method that handles binary packet parsing.
Definition: pkt6.cc:464
OptionCollection getAllRelayOptions(const uint16_t option_code, const RelaySearchOrder &order)
Return first instances of a specified option.
Definition: pkt6.cc:201
virtual HWAddrPtr getMACFromDUID()
Extract MAC/Hardware address from client-id.
Definition: pkt6.cc:643
virtual HWAddrPtr getMACFromDocsisCMTS()
Attempts to extract MAC/Hardware address from DOCSIS options.
Definition: pkt6.cc:971
const isc::asiolink::IOAddress & getRelay6LinkAddress(uint8_t relay_level) const
return the link address field from a relay option
Definition: pkt6.cc:326
OptionCollection getNonCopiedAllRelayOptions(const uint16_t option_code, const RelaySearchOrder &order) const
Returns pointers to instances of specified option.
Definition: pkt6.cc:171
std::vector< RelayInfo > relay_info_
Relay information.
Definition: pkt6.h:473
uint8_t msg_type_
DHCPv6 message type.
Definition: pkt6.h:620
Pkt6(uint8_t msg_type, uint32_t transid, DHCPv6Proto proto=UDP)
Constructor, used in replying to a message.
Definition: pkt6.cc:58
void addRelayInfo(const RelayInfo &relay)
add information about one traversed relay
Definition: pkt6.cc:627
virtual HWAddrPtr getMACFromDocsisModem()
Attempts to extract MAC/Hardware address from DOCSIS options inserted by the modem itself.
Definition: pkt6.cc:943
OptionCollection getNonCopiedRelayOptions(const uint16_t opt_type, const uint8_t relay_level) const
Returns all option instances inserted by relay agent.
Definition: pkt6.cc:281
void packUDP()
Builds on wire packet for UDP transmission.
Definition: pkt6.cc:394
void unpackUDP()
Parses on-wire form of UDP DHCPv6 packet.
Definition: pkt6.cc:476
void unpackMsg(OptionBuffer::const_iterator begin, OptionBuffer::const_iterator end)
Unpacks direct (non-relayed) message.
Definition: pkt6.cc:506
uint16_t directLen() const
Calculates size of the message as if it was not relayed at all.
Definition: pkt6.cc:368
uint16_t getRelayOverhead(const RelayInfo &relay) const
Calculates overhead introduced in specified relay.
Definition: pkt6.cc:345
virtual uint8_t getType() const
Returns message type (e.g.
Definition: pkt6.h:220
static const size_t DHCPV6_RELAY_HDR_LEN
specifies relay DHCPv6 packet header length (over UDP)
Definition: pkt6.h:50
Base class for classes representing DHCP messages.
Definition: pkt.h:96
isc::asiolink::IOAddress remote_addr_
Remote IP address.
Definition: pkt.h:815
uint16_t local_port_
local TDP or UDP port
Definition: pkt.h:818
uint32_t transid_
Transaction-id (32 bits for v4, 24 bits for v6)
Definition: pkt.h:793
isc::asiolink::IOAddress local_addr_
Local IP (v4 or v6) address.
Definition: pkt.h:809
OptionBuffer data_
Unparsed data (in received packets).
Definition: pkt.h:340
uint16_t remote_port_
remote TCP or UDP port
Definition: pkt.h:821
uint32_t getTransid() const
Returns value of transaction-id field.
Definition: pkt.h:276
isc::dhcp::OptionCollection options_
Collection of options present in this message.
Definition: pkt.h:681
isc::util::OutputBuffer buffer_out_
Output buffer (used during message transmission)
Definition: pkt.h:831
OptionCollection getNonCopiedOptions(const uint16_t opt_type) const
Returns all option instances of specified type without copying.
Definition: pkt.cc:77
bool copy_retrieved_options_
Indicates if a copy of the retrieved option should be returned when Pkt::getOption is called.
Definition: pkt.h:837
std::string iface_
Name of the network interface the packet was received/to be sent over.
Definition: pkt.h:796
OptionPtr getNonCopiedOption(const uint16_t type) const
Returns the first option of specified type without copying.
Definition: pkt.cc:55
HWAddrPtr getMACFromIPv6(const isc::asiolink::IOAddress &addr)
Attempts to convert IPv6 address into MAC.
Definition: pkt.cc:271
void writeUint8(uint8_t data)
Write an unsigned 8-bit integer into the buffer.
Definition: buffer.h:466
void writeUint16(uint16_t data)
Write an unsigned 16-bit integer in host byte order into the buffer in network byte order.
Definition: buffer.h:490
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition: buffer.h:550
void clear()
Clear buffer content.
Definition: buffer.h:451
@ D6O_INTERFACE_ID
Definition: dhcp6.h:38
@ D6O_CLIENTID
Definition: dhcp6.h:21
@ D6O_RELAY_MSG
Definition: dhcp6.h:29
@ D6O_REMOTE_ID
Definition: dhcp6.h:57
@ D6O_VENDOR_OPTS
Definition: dhcp6.h:37
@ D6O_RELAY_SOURCE_PORT
Definition: dhcp6.h:155
@ D6O_CLIENT_LINKLAYER_ADDR
Definition: dhcp6.h:99
@ DHCPV6_ADVERTISE
Definition: dhcp6.h:199
@ DHCPV6_LEASEQUERY
Definition: dhcp6.h:212
@ DHCPV6_REQUEST
Definition: dhcp6.h:200
@ DHCPV6_RELAY_REPL
Definition: dhcp6.h:210
@ DHCPV6_RENEW
Definition: dhcp6.h:202
@ DHCPV6_DHCPV4_QUERY
Definition: dhcp6.h:221
@ DHCPV6_DHCPV4_RESPONSE
Definition: dhcp6.h:222
@ DHCPV6_RECONFIGURE
Definition: dhcp6.h:207
@ DHCPV6_REBIND
Definition: dhcp6.h:203
@ DHCPV6_REPLY
Definition: dhcp6.h:204
@ DHCPV6_SOLICIT
Definition: dhcp6.h:198
@ DHCPV6_RELEASE
Definition: dhcp6.h:205
@ DHCPV6_LEASEQUERY_DATA
Definition: dhcp6.h:216
@ DHCPV6_INFORMATION_REQUEST
Definition: dhcp6.h:208
@ DHCPV6_CONFIRM
Definition: dhcp6.h:201
@ DHCPV6_LEASEQUERY_DONE
Definition: dhcp6.h:215
@ DHCPV6_LEASEQUERY_REPLY
Definition: dhcp6.h:213
@ DHCPV6_RELAY_FORW
Definition: dhcp6.h:209
@ DHCPV6_DECLINE
Definition: dhcp6.h:206
#define HOP_COUNT_LIMIT
Definition: dhcp6.h:314
#define VENDOR_ID_CABLE_LABS
#define DOCSIS3_V6_CMTS_CM_MAC
#define DOCSIS3_V6_DEVICE_ID
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
static const uint32_t HWADDR_SOURCE_REMOTE_ID
A relay can insert remote-id.
Definition: hwaddr.h:63
static const uint32_t HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION
Get it from RFC6939 option.
Definition: hwaddr.h:59
static const uint32_t HWADDR_SOURCE_DOCSIS_MODEM
A cable modem (acting as DHCP client) that supports DOCSIS standard can insert DOCSIS options that co...
Definition: hwaddr.h:79
static const uint32_t HWADDR_SOURCE_DUID
Extracted from DUID-LL or DUID-LLT (not 100% reliable as the client can send fake DUID).
Definition: hwaddr.h:48
static const uint32_t HWADDR_SOURCE_DOCSIS_CMTS
A CMTS (acting as DHCP relay agent) that supports DOCSIS standard can insert DOCSIS options that cont...
Definition: hwaddr.h:73
@ info
Definition: db_log.h:118
boost::shared_ptr< OptionVendor > OptionVendorPtr
Pointer to a vendor option.
boost::shared_ptr< Iface > IfacePtr
Type definition for the pointer to an Iface object.
Definition: iface_mgr.h:487
boost::shared_ptr< DUID > DuidPtr
Definition: duid.h:131
std::multimap< unsigned int, OptionPtr > OptionCollection
A collection of DHCP (v4 or v6) options.
Definition: option.h:40
boost::shared_ptr< HWAddr > HWAddrPtr
Shared pointer to a hardware address structure.
Definition: hwaddr.h:154
boost::shared_ptr< Pkt6 > Pkt6Ptr
A pointer to Pkt6 packet.
Definition: pkt6.h:28
std::vector< uint8_t > OptionBuffer
buffer types used in DHCP code.
Definition: option.h:24
@ HTYPE_DOCSIS
The traffic captures we have from cable modems as well as this list by IANA: http://www....
Definition: dhcp4.h:57
boost::shared_ptr< Option > OptionPtr
Definition: option.h:36
uint16_t readUint16(const void *buffer, size_t length)
Read Unsigned 16-Bit Integer from Buffer.
Definition: io_utilities.h:28
Defines the logger used by the top-level component of kea-lfc.
const IOAddress DEFAULT_ADDRESS6("::")
Default address used in Pkt6 constructor.
#define DHCP6_OPTION_SPACE
Hardware type that represents information from DHCPv4 packet.
Definition: hwaddr.h:20
static const size_t MAX_HWADDR_LEN
Maximum size of a hardware address.
Definition: hwaddr.h:27
structure that describes a single relay information
Definition: pkt6.h:85
isc::dhcp::OptionCollection options_
options received from a specified relay, except relay-msg option
Definition: pkt6.h:104
uint8_t msg_type_
message type (RELAY-FORW oro RELAY-REPL)
Definition: pkt6.h:94
std::string toText() const
Returns printable representation of the relay information.
Definition: pkt6.cc:40
isc::asiolink::IOAddress linkaddr_
fixed field in relay-forw/relay-reply
Definition: pkt6.h:96
uint8_t hop_count_
number of traversed relays (up to 32)
Definition: pkt6.h:95
isc::asiolink::IOAddress peeraddr_
fixed field in relay-forw/relay-reply
Definition: pkt6.h:97