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