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