Kea 2.2.0
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>
19#include <dhcp/duid.h>
20#include <dhcp/iface_mgr.h>
21
22#include <iterator>
23#include <iostream>
24#include <sstream>
25
26using namespace std;
27using namespace isc::asiolink;
28
31
32namespace isc {
33namespace dhcp {
34
36 : msg_type_(0), hop_count_(0), linkaddr_(DEFAULT_ADDRESS6),
37 peeraddr_(DEFAULT_ADDRESS6), relay_msg_len_(0) {
38}
39
40std::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
53Pkt6::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
58Pkt6::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
63size_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
75void
76Pkt6::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
107Pkt6::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
139Pkt6::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
172Pkt6::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
190Pkt6::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
211Pkt6::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
221Pkt6::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
230uint16_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
253uint16_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
264void
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
278void
280 try {
281 // Make sure that the buffer is empty before we start writing to it.
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
341void
344 isc_throw(NotImplemented, "DHCPv6 over TCP (bulk leasequery and failover)"
345 " not implemented yet.");
346}
347
348void
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
360void
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:
380 default: // assume that unknown messages are not using relay format
381 {
382 return (unpackMsg(data_.begin(), data_.end()));
383 }
386 return (unpackRelayMsg());
387 }
388}
389
390void
391Pkt6::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
434void
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
511void
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
521void
523 isc_throw(Unexpected, "DHCPv6 over TCP (bulk leasequery and failover) "
524 "not implemented yet.");
525}
526
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
579std::string
580Pkt6::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
592std::string
593Pkt6::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
610std::string
616 return (makeLabel(getClientId(), getTransid(), HWAddrPtr()));}
617
618std::string
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
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
665Pkt6::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
672Pkt6::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
691const char*
692Pkt6::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
726 return (LEASEQUERY);
727
729 return (LEASEQUERY_REPLY);
730
731 case DHCPV6_REBIND:
732 return (REBIND);
733
735 return (RECONFIGURE);
736
738 return (RELAY_FORW);
739
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
759 return (DHCPV4_QUERY);
760
762 return (DHCPV4_RESPONSE);
763
764 default:
765 ;
766 }
767 return (UNKNOWN);
768}
769
770const char* Pkt6::getName() const {
771 return (getName(getType()));
772}
773
774void 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) {
780 info.msg_type_ = DHCPV6_RELAY_REPL;
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
809 if (relay_info_.empty()) {
810 // This is a direct message, use source address
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
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
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));
861 }
862 }
863 }
864
865 return mac;
866}
867
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
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));
888 }
889 }
890 }
891
892 return (mac);
893}
894
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
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
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:27
@ DUID_LL
link-layer, see RFC3315, section 11.4
Definition: duid.h:42
@ DUID_LLT
link-layer + time, see RFC3315, section 11.2
Definition: duid.h:40
static IfaceMgr & instance()
IfaceMgr is a singleton class.
Definition: iface_mgr.cc:53
IfacePtr getIface(int ifindex)
Returns interface specified interface index.
Definition: iface_mgr.cc:902
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:308
static void packOptions6(isc::util::OutputBuffer &buf, const isc::dhcp::OptionCollection &options)
Stores DHCPv6 options in a buffer.
Definition: libdhcp++.cc:1069
This class represents vendor-specific information option.
Definition: option_vendor.h:30
OptionPtr getOption(uint16_t type) const
Returns shared_ptr to suboption of specific type.
Definition: option.cc:199
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:619
uint16_t calculateRelaySizes()
Calculates overhead for all relays defined for this message.
Definition: pkt6.cc:241
virtual HWAddrPtr getMACFromIPv6RelayOpt()
Extract MAC/Hardware address from client link-layer address.
Definition: pkt6.cc:819
OptionCollection getNonCopiedOptions(const uint16_t opt_type) const
Returns all option instances of specified type without copying.
Definition: pkt6.cc:665
DHCPv6Proto
DHCPv6 transport protocol.
Definition: pkt6.h:53
virtual std::string getLabel() const
Returns text representation of the primary packet identifiers.
Definition: pkt6.cc:611
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:265
OptionPtr getRelayOption(uint16_t option_code, uint8_t nesting_level)
Returns option inserted by relay.
Definition: pkt6.cc:190
virtual HWAddrPtr getMACFromRemoteIdRelayOption()
Attempts to obtain MAC address from remote-id relay option.
Definition: pkt6.cc:896
void unpackTCP()
Parses on-wire form of TCP DHCPv6 packet.
Definition: pkt6.cc:522
const char * getName() const
Returns name of the DHCPv6 message.
Definition: pkt6.cc:770
virtual HWAddrPtr getMACFromSrcLinkLocalAddr()
Attempts to generate MAC/Hardware address from IPv6 link-local address.
Definition: pkt6.cc:808
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
void unpackRelayMsg()
Unpacks relayed message (RELAY-FORW or RELAY-REPL).
Definition: pkt6.cc:435
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:580
OptionPtr getAnyRelayOption(const uint16_t option_code, const RelaySearchOrder &order)
Return first instance of a specified option.
Definition: pkt6.cc:139
DuidPtr getClientId() const
Retrieves the DUID from the Client Identifier option.
Definition: pkt6.cc:648
void packTCP()
Builds on wire packet for TCP transmission.
Definition: pkt6.cc:342
OptionPtr getNonCopiedAnyRelayOption(const uint16_t option_code, const RelaySearchOrder &order) const
Returns pointer to an instance of specified option.
Definition: pkt6.cc:107
isc::dhcp::OptionCollection getOptions(const uint16_t type)
Returns all instances of specified type.
Definition: pkt6.cc:672
void copyRelayInfo(const Pkt6Ptr &question)
copies relay information from client's packet to server's response
Definition: pkt6.cc:774
DHCPv6Proto proto_
UDP (usually) or TCP (bulk leasequery or failover)
Definition: pkt6.h:580
const isc::asiolink::IOAddress & getRelay6PeerAddress(uint8_t relay_level) const
return the peer address field from a relay option
Definition: pkt6.cc:221
virtual void unpack()
Dispatch method that handles binary packet parsing.
Definition: pkt6.cc:349
virtual HWAddrPtr getMACFromDUID()
Extract MAC/Hardware address from client-id.
Definition: pkt6.cc:528
virtual HWAddrPtr getMACFromDocsisCMTS()
Attempts to extract MAC/Hardware address from DOCSIS options.
Definition: pkt6.cc:869
const isc::asiolink::IOAddress & getRelay6LinkAddress(uint8_t relay_level) const
return the link address field from a relay option
Definition: pkt6.cc:211
std::vector< RelayInfo > relay_info_
Relay information.
Definition: pkt6.h:436
uint8_t msg_type_
DHCPv6 message type.
Definition: pkt6.h:583
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:512
virtual HWAddrPtr getMACFromDocsisModem()
Attempts to extract MAC/Hardware address from DOCSIS options inserted by the modem itself.
Definition: pkt6.cc:847
void packUDP()
Builds on wire packet for UDP transmission.
Definition: pkt6.cc:279
void unpackUDP()
Parses on-wire form of UDP DHCPv6 packet.
Definition: pkt6.cc:361
void unpackMsg(OptionBuffer::const_iterator begin, OptionBuffer::const_iterator end)
Unpacks direct (non-relayed) message.
Definition: pkt6.cc:391
uint16_t directLen() const
Calculates size of the message as if it was not relayed at all.
Definition: pkt6.cc:253
uint16_t getRelayOverhead(const RelayInfo &relay) const
Calculates overhead introduced in specified relay.
Definition: pkt6.cc:230
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:90
isc::asiolink::IOAddress remote_addr_
Remote IP address.
Definition: pkt.h:748
uint16_t local_port_
local TDP or UDP port
Definition: pkt.h:751
uint32_t transid_
Transaction-id (32 bits for v4, 24 bits for v6)
Definition: pkt.h:726
isc::asiolink::IOAddress local_addr_
Local IP (v4 or v6) address.
Definition: pkt.h:742
OptionBuffer data_
Unparsed data (in received packets).
Definition: pkt.h:312
uint16_t remote_port_
remote TCP or UDP port
Definition: pkt.h:754
uint32_t getTransid() const
Returns value of transaction-id field.
Definition: pkt.h:266
isc::dhcp::OptionCollection options_
Collection of options present in this message.
Definition: pkt.h:614
isc::util::OutputBuffer buffer_out_
Output buffer (used during message transmission)
Definition: pkt.h:764
bool copy_retrieved_options_
Indicates if a copy of the retrieved option should be returned when Pkt::getOption is called.
Definition: pkt.h:770
std::string iface_
Name of the network interface the packet was received/to be sent over.
Definition: pkt.h:729
OptionPtr getNonCopiedOption(const uint16_t type) const
Returns the first option of specified type without copying.
Definition: pkt.cc:46
HWAddrPtr getMACFromIPv6(const isc::asiolink::IOAddress &addr)
Attempts to convert IPv6 address into MAC.
Definition: pkt.cc:226
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:206
@ DHCPV6_LEASEQUERY
Definition: dhcp6.h:219
@ DHCPV6_REQUEST
Definition: dhcp6.h:207
@ DHCPV6_RELAY_REPL
Definition: dhcp6.h:217
@ DHCPV6_RENEW
Definition: dhcp6.h:209
@ DHCPV6_DHCPV4_QUERY
Definition: dhcp6.h:228
@ DHCPV6_DHCPV4_RESPONSE
Definition: dhcp6.h:229
@ DHCPV6_RECONFIGURE
Definition: dhcp6.h:214
@ DHCPV6_REBIND
Definition: dhcp6.h:210
@ DHCPV6_REPLY
Definition: dhcp6.h:211
@ DHCPV6_SOLICIT
Definition: dhcp6.h:205
@ DHCPV6_RELEASE
Definition: dhcp6.h:212
@ DHCPV6_INFORMATION_REQUEST
Definition: dhcp6.h:215
@ DHCPV6_CONFIRM
Definition: dhcp6.h:208
@ DHCPV6_LEASEQUERY_REPLY
Definition: dhcp6.h:220
@ DHCPV6_RELAY_FORW
Definition: dhcp6.h:216
@ DHCPV6_DECLINE
Definition: dhcp6.h:213
#define HOP_COUNT_LIMIT
Definition: dhcp6.h:329
#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:117
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:20
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
RelayInfo()
default constructor
Definition: pkt6.cc:35
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