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