Kea 2.5.8
pkt6.cc
Go to the documentation of this file.
1// Copyright (C) 2011-2024 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:
491 default: // assume that unknown messages are not using relay format
492 {
493 return (unpackMsg(data_.begin(), data_.end()));
494 }
497 return (unpackRelayMsg());
498 }
499}
500
501void
502Pkt6::unpackMsg(OptionBuffer::const_iterator begin,
503 OptionBuffer::const_iterator end) {
504 size_t size = std::distance(begin, end);
505 if (size < 4) {
506 // truncated message (less than 4 bytes)
507 isc_throw(BadValue, "Received truncated UDP DHCPv6 packet of size "
508 << data_.size() << ", DHCPv6 header alone has 4 bytes.");
509 }
510
511 msg_type_ = *begin++;
512
513 transid_ = ( (*begin++) << 16 ) +
514 ((*begin++) << 8) + (*begin++);
515 transid_ = transid_ & 0xffffff;
516
517 // See below about invoking Postel's law, as we aren't using
518 // size we don't need to update it. If we do so in the future
519 // perhaps for stats gathering we can uncomment this.
520 // size -= sizeof(uint32_t); // We just parsed 4 bytes header
521
522 OptionBuffer opt_buffer(begin, end);
523
524 // If custom option parsing function has been set, use this function
525 // to parse options. Otherwise, use standard function from libdhcp.
526 size_t offset = LibDHCP::unpackOptions6(opt_buffer, DHCP6_OPTION_SPACE, options_);
527
528 // If offset is not equal to the size, then something is wrong here. We
529 // either parsed past input buffer (bug in our code) or we haven't parsed
530 // everything (received trailing garbage or truncated option).
531 //
532 // Invoking Jon Postel's law here: be conservative in what you send, and be
533 // liberal in what you accept. There's no easy way to log something from
534 // libdhcp++ library, so we just choose to be silent about remaining
535 // bytes. We also need to quell compiler warning about unused offset
536 // variable.
537 //
538 // if (offset != size) {
539 // isc_throw(BadValue, "Received DHCPv6 buffer of size " << size
540 // << ", were able to parse " << offset << " bytes.");
541 // }
542 (void)offset;
543}
544
545void
547
548 // we use offset + bufsize, because we want to avoid creating unnecessary
549 // copies. There may be up to 32 relays. While using InputBuffer would
550 // be probably a bit cleaner, copying data up to 32 times is unacceptable
551 // price here. Hence a single buffer with offsets and lengths.
552 size_t bufsize = data_.size();
553 size_t offset = 0;
554
555 while (bufsize >= DHCPV6_RELAY_HDR_LEN) {
556
557 RelayInfo relay;
558
559 size_t relay_msg_offset = 0;
560 size_t relay_msg_len = 0;
561
562 // parse fixed header first (first 34 bytes)
563 relay.msg_type_ = data_[offset++];
564 relay.hop_count_ = data_[offset++];
565 relay.linkaddr_ = IOAddress::fromBytes(AF_INET6, &data_[offset]);
566 offset += isc::asiolink::V6ADDRESS_LEN;
567 relay.peeraddr_ = IOAddress::fromBytes(AF_INET6, &data_[offset]);
568 offset += isc::asiolink::V6ADDRESS_LEN;
569 bufsize -= DHCPV6_RELAY_HDR_LEN; // 34 bytes (1+1+16+16)
570
571 // parse the rest as options
572 OptionBuffer opt_buffer(&data_[offset], &data_[offset] + bufsize);
573
574 // If custom option parsing function has been set, use this function
575 // to parse options. Otherwise, use standard function from libdhcp.
577 &relay_msg_offset, &relay_msg_len);
578
580 //relay.interface_id_ = options->getOption(D6O_INTERFACE_ID);
581 //relay.subscriber_id_ = options->getOption(D6O_SUBSCRIBER_ID);
582 //relay.remote_id_ = options->getOption(D6O_REMOTE_ID);
583
584 if (relay_msg_offset == 0 || relay_msg_len == 0) {
585 isc_throw(BadValue, "Mandatory relay-msg option missing");
586 }
587
588 // store relay information parsed so far
589 addRelayInfo(relay);
590
592
593 if (relay_msg_len >= bufsize) {
594 // length of the relay_msg option extends beyond end of the message
595 isc_throw(Unexpected, "Relay-msg option is truncated.");
596 }
597 uint8_t inner_type = data_[offset + relay_msg_offset];
598 offset += relay_msg_offset; // offset is relative
599 bufsize = relay_msg_len; // length is absolute
600
601 if ( (inner_type != DHCPV6_RELAY_FORW) &&
602 (inner_type != DHCPV6_RELAY_REPL)) {
603 // Ok, the inner message is not encapsulated, let's decode it
604 // directly
605 return (unpackMsg(data_.begin() + offset, data_.begin() + offset
606 + relay_msg_len));
607 }
608
609 // Oh well, there's inner relay-forw or relay-repl inside. Let's
610 // unpack it as well. The next loop iteration will take care
611 // of that.
612 }
613
614 if ( (offset == data_.size()) && (bufsize == 0) ) {
615 // message has been parsed completely
616 return;
617 }
618
620}
621
622void
624 if (relay_info_.size() > HOP_COUNT_LIMIT) {
625 isc_throw(BadValue, "Massage cannot be encapsulated more than 32 times");
626 }
627
629 relay_info_.push_back(relay);
630}
631
632void
634 isc_throw(Unexpected, "DHCPv6 over TCP (bulk leasequery and failover) "
635 "not implemented yet.");
636}
637
640 HWAddrPtr mac;
642 if (!opt_duid) {
643 return (mac);
644 }
645
646 uint8_t hlen = opt_duid->getData().size();
647 if (!hlen) {
648 return (mac);
649 }
650 vector<uint8_t> hw_addr(hlen, 0);
651 std::vector<unsigned char> duid_data = opt_duid->getData();
652
653 // Read the first two bytes. That duid type.
654 uint16_t duid_type = util::readUint16(&duid_data[0], duid_data.size());
655
656 switch (duid_type) {
657 case DUID::DUID_LL:
658 {
659 // 2 bytes of duid type, 2 bytes of hardware type and at least
660 // 1 byte of actual identification
661 if (duid_data.size() >= 5) {
662 uint16_t hwtype = util::readUint16(&duid_data[2],
663 duid_data.size() - 2);
664 mac.reset(new HWAddr(&duid_data[4], duid_data.size() - 4, hwtype));
665 }
666 break;
667 }
668 case DUID::DUID_LLT:
669 {
670 // 2 bytes of duid type, 2 bytes of hardware, 4 bytes for timestamp,
671 // and at least 1 byte of actual identification
672 if (duid_data.size() >= 9) {
673 uint16_t hwtype = util::readUint16(&duid_data[2],
674 duid_data.size() - 2);
675 mac.reset(new HWAddr(&duid_data[8], duid_data.size() - 8, hwtype));
676 }
677 break;
678 }
679 default:
680 break;
681 }
682
683 if (mac) {
684 mac->source_ = HWAddr::HWADDR_SOURCE_DUID;
685 }
686
687 return (mac);
688}
689
690std::string
691Pkt6::makeLabel(const DuidPtr duid, const uint32_t transid,
692 const HWAddrPtr& hwaddr) {
693 // Create label with DUID and HW address.
694 std::stringstream label;
695 label << makeLabel(duid, hwaddr);
696
697 // Append transaction id.
698 label << ", tid=0x" << std::hex << transid << std::dec;
699
700 return (label.str());
701}
702
703std::string
704Pkt6::makeLabel(const DuidPtr duid, const HWAddrPtr& hwaddr) {
705 std::stringstream label;
706 // DUID should be present at all times, so explicitly inform when
707 // it is no present (no info).
708 // HW address is typically not carried in the DHCPv6 messages
709 // and can be extracted using various, but not fully reliable,
710 // techniques.
711 label << "duid=[" << (duid ? duid->toText() : "no info")
712 << "], [" << (hwaddr ? hwaddr->toText() : "no hwaddr info") << "]";
713
714 return (label.str());
715}
716
717std::string
723 return (makeLabel(getClientId(), getTransid(), HWAddrPtr()));}
724
725std::string
727 stringstream tmp;
728
729 // First print the basics
730 tmp << "local_address=[" << local_addr_ << "]:" << local_port_
731 << ", remote_address=[" << remote_addr_ << "]:" << remote_port_ << "," << endl;
732
733 tmp << "msg_type=" << getName(msg_type_) << " (" << static_cast<int>(msg_type_) << ")";
734 tmp << ", trans_id=0x" << hex << transid_ << dec;
735
736 if (!options_.empty()) {
737 tmp << "," << endl << "options:";
738 for (auto const& opt : options_) {
739 try {
740 tmp << endl << opt.second->toText(2);
741 } catch (...) {
742 tmp << "(unknown)" << endl;
743 }
744 }
745
746 } else {
747 tmp << "," << endl << "message contains no options";
748 }
749
750 // Finally, print the relay information (if present)
751 if (!relay_info_.empty()) {
752 tmp << endl << relay_info_.size() << " relay(s):" << endl;
753 int cnt = 0;
754 for (auto const& relay : relay_info_) {
755 tmp << "relay[" << cnt++ << "]: " << relay.toText();
756 }
757 } else {
758 tmp << endl << "No relays traversed." << endl;
759 }
760
761 return (tmp.str());
762}
763
767 try {
768 // This will throw if the DUID length is larger than 128 bytes
769 // or is too short.
770 return (opt_duid ? DuidPtr(new DUID(opt_duid->getData())) : DuidPtr());
771 } catch (...) {
772 // Do nothing. This method is used only by getLabel(), which is
773 // used for logging purposes. We should not throw, but rather
774 // report no DUID. We should not log anything, as we're in the
775 // process of logging something for this packet. So the only
776 // choice left is to return an empty pointer.
777 }
778 return (DuidPtr());
779}
780
781const char*
782Pkt6::getName(const uint8_t type) {
783 static const char* ADVERTISE = "ADVERTISE";
784 static const char* CONFIRM = "CONFIRM";
785 static const char* DECLINE = "DECLINE";
786 static const char* INFORMATION_REQUEST = "INFORMATION_REQUEST";
787 static const char* LEASEQUERY = "LEASEQUERY";
788 static const char* LEASEQUERY_DATA = "LEASEQUERY_DATA";
789 static const char* LEASEQUERY_DONE = "LEASEQUERY_DONE";
790 static const char* LEASEQUERY_REPLY = "LEASEQUERY_REPLY";
791 static const char* REBIND = "REBIND";
792 static const char* RECONFIGURE = "RECONFIGURE";
793 static const char* RELAY_FORW = "RELAY_FORWARD";
794 static const char* RELAY_REPL = "RELAY_REPLY";
795 static const char* RELEASE = "RELEASE";
796 static const char* RENEW = "RENEW";
797 static const char* REPLY = "REPLY";
798 static const char* REQUEST = "REQUEST";
799 static const char* SOLICIT = "SOLICIT";
800 static const char* DHCPV4_QUERY = "DHCPV4_QUERY";
801 static const char* DHCPV4_RESPONSE = "DHCPV4_RESPONSE";
802 static const char* UNKNOWN = "UNKNOWN";
803
804 switch (type) {
805 case DHCPV6_ADVERTISE:
806 return (ADVERTISE);
807
808 case DHCPV6_CONFIRM:
809 return (CONFIRM);
810
811 case DHCPV6_DECLINE:
812 return (DECLINE);
813
815 return (INFORMATION_REQUEST);
816
818 return (LEASEQUERY);
819
821 return (LEASEQUERY_DATA);
822
824 return (LEASEQUERY_DONE);
825
827 return (LEASEQUERY_REPLY);
828
829 case DHCPV6_REBIND:
830 return (REBIND);
831
833 return (RECONFIGURE);
834
836 return (RELAY_FORW);
837
839 return (RELAY_REPL);
840
841 case DHCPV6_RELEASE:
842 return (RELEASE);
843
844 case DHCPV6_RENEW:
845 return (RENEW);
846
847 case DHCPV6_REPLY:
848 return (REPLY);
849
850 case DHCPV6_REQUEST:
851 return (REQUEST);
852
853 case DHCPV6_SOLICIT:
854 return (SOLICIT);
855
857 return (DHCPV4_QUERY);
858
860 return (DHCPV4_RESPONSE);
861
862 default:
863 ;
864 }
865 return (UNKNOWN);
866}
867
868const char* Pkt6::getName() const {
869 return (getName(getType()));
870}
871
872void Pkt6::copyRelayInfo(const Pkt6Ptr& question) {
873
874 // We use index rather than iterator, because we need that as a parameter
875 // passed to getNonCopiedRelayOption()
876 for (size_t i = 0; i < question->relay_info_.size(); ++i) {
878 info.msg_type_ = DHCPV6_RELAY_REPL;
879 info.hop_count_ = question->relay_info_[i].hop_count_;
880 info.linkaddr_ = question->relay_info_[i].linkaddr_;
881 info.peeraddr_ = question->relay_info_[i].peeraddr_;
882
883 // Is there an interface-id option in this nesting level?
884 // If there is, we need to echo it back
885 OptionPtr opt = question->getNonCopiedRelayOption(D6O_INTERFACE_ID, i);
886 // taken from question->RelayInfo_[i].options_
887 if (opt) {
888 info.options_.insert(make_pair(opt->getType(), opt));
889 }
890
891 // Same for relay-source-port option
892 opt = question->getNonCopiedRelayOption(D6O_RELAY_SOURCE_PORT, i);
893 if (opt) {
894 info.options_.insert(make_pair(opt->getType(), opt));
895 }
896
898
899 // Add this relay-forw info (client's message) to our relay-repl
900 // message (server's response)
901 relay_info_.push_back(info);
902 }
903}
904
907 if (relay_info_.empty()) {
908 // This is a direct message, use source address
910 }
911
912 // This is a relayed message, get the peer-addr from the first relay-forw
913 return (getMACFromIPv6(relay_info_[relay_info_.size() - 1].peeraddr_));
914}
915
918 HWAddrPtr mac;
919
920 // This is not a direct message
921 if (!relay_info_.empty()) {
922 // RFC6969 Section 6: Look for the client_linklayer_addr option on the
923 // relay agent closest to the client
926 if (opt) {
927 const OptionBuffer data = opt->getData();
928 // This client link address option is supposed to be
929 // 2 bytes of link-layer type followed by link-layer address.
930 if (data.size() >= 3) {
931 // +2, -2 means to skip the initial 2 bytes which are
932 // hwaddress type
933 mac.reset(new HWAddr(&data[0] + 2, data.size() - 2,
934 opt->getUint16()));
935
937 }
938 }
939 }
940
941 return mac;
942}
943
946 HWAddrPtr mac;
947 OptionVendorPtr vendor;
948 for (auto const& opt : getNonCopiedOptions(D6O_VENDOR_OPTS)) {
949 if (opt.first != D6O_VENDOR_OPTS) {
950 continue;
951 }
952 vendor = boost::dynamic_pointer_cast< OptionVendor>(opt.second);
953 // Check if this is indeed DOCSIS3 environment
954 if (!vendor || vendor->getVendorId() != VENDOR_ID_CABLE_LABS) {
955 continue;
956 }
957 // If it is, try to get device-id option
958 OptionPtr device_id = vendor->getOption(DOCSIS3_V6_DEVICE_ID);
959 if (device_id) {
960 // If the option contains any data, use it as MAC address
961 if (!device_id->getData().empty()) {
962 mac.reset(new HWAddr(device_id->getData(), HTYPE_DOCSIS));
964 break;
965 }
966 }
967 }
968
969 return mac;
970}
971
974 if (relay_info_.empty()) {
975 return (HWAddrPtr());
976 }
977
978 // If the message passed through a CMTS, there'll
979 // CMTS-specific options in it.
980 HWAddrPtr mac;
981 OptionVendorPtr vendor;
982 for (auto const& opt : getAllRelayOptions(D6O_VENDOR_OPTS,
984 if (opt.first != D6O_VENDOR_OPTS) {
985 continue;
986 }
987 vendor = boost::dynamic_pointer_cast< OptionVendor>(opt.second);
988 // Check if this is indeed DOCSIS3 environment
989 if (!vendor || vendor->getVendorId() != VENDOR_ID_CABLE_LABS) {
990 continue;
991 }
992 // Try to get cable modem mac
993 OptionPtr cm_mac = vendor->getOption(DOCSIS3_V6_CMTS_CM_MAC);
994
995 // If the option contains any data, use it as MAC address
996 if (cm_mac && !cm_mac->getData().empty()) {
997 mac.reset(new HWAddr(cm_mac->getData(), HTYPE_DOCSIS));
999 break;
1000 }
1001 }
1002
1003 return (mac);
1004}
1005
1008 HWAddrPtr mac;
1009
1010 // If this is relayed message
1011 if (!relay_info_.empty()) {
1012 // Get remote-id option from a relay agent closest to the client
1014 if (opt) {
1015 const OptionBuffer data = opt->getData();
1016 // This remote-id option is supposed to be 4 bytes of
1017 // of enterprise-number followed by remote-id.
1018 if (data.size() >= 5) {
1019 // Let's get the interface this packet was received on.
1020 // We need it to get the hardware type.
1022 uint16_t hwtype = 0; // not specified
1023
1024 // If we get the interface HW type, great! If not,
1025 // let's not panic.
1026 if (iface) {
1027 hwtype = iface->getHWType();
1028 }
1029
1030 size_t len = data.size() - 4;
1031
1034 }
1035
1036 // Skip the initial 4 bytes which are enterprise-number.
1037 mac.reset(new HWAddr(&data[0] + 4, len, hwtype));
1038 mac->source_ = HWAddr::HWADDR_SOURCE_REMOTE_ID;
1039 }
1040 }
1041 }
1042
1043 return (mac);
1044}
1045
1046} // end of namespace isc::dhcp
1047} // 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:311
static void packOptions6(isc::util::OutputBuffer &buf, const isc::dhcp::OptionCollection &options)
Stores DHCPv6 options in a buffer.
Definition: libdhcp++.cc:1238
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:726
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:917
DHCPv6Proto
DHCPv6 transport protocol.
Definition: pkt6.h:53
virtual std::string getLabel() const
Returns text representation of the primary packet identifiers.
Definition: pkt6.cc:718
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:1007
void unpackTCP()
Parses on-wire form of TCP DHCPv6 packet.
Definition: pkt6.cc:633
const char * getName() const
Returns name of the DHCPv6 message.
Definition: pkt6.cc:868
virtual HWAddrPtr getMACFromSrcLinkLocalAddr()
Attempts to generate MAC/Hardware address from IPv6 link-local address.
Definition: pkt6.cc:906
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:546
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:691
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:765
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:872
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:639
virtual HWAddrPtr getMACFromDocsisCMTS()
Attempts to extract MAC/Hardware address from DOCSIS options.
Definition: pkt6.cc:973
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:623
virtual HWAddrPtr getMACFromDocsisModem()
Attempts to extract MAC/Hardware address from DOCSIS options inserted by the modem itself.
Definition: pkt6.cc:945
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:502
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:930
uint16_t local_port_
local TDP or UDP port
Definition: pkt.h:933
uint32_t transid_
Transaction-id (32 bits for v4, 24 bits for v6)
Definition: pkt.h:908
isc::asiolink::IOAddress local_addr_
Local IP (v4 or v6) address.
Definition: pkt.h:924
OptionBuffer data_
Unparsed data (in received packets).
Definition: pkt.h:404
uint16_t remote_port_
remote TCP or UDP port
Definition: pkt.h:936
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:796
isc::util::OutputBuffer buffer_out_
Output buffer (used during message transmission)
Definition: pkt.h:946
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:952
std::string iface_
Name of the network interface the packet was received/to be sent over.
Definition: pkt.h:911
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:277
void writeUint8(uint8_t data)
Write an unsigned 8-bit integer into the buffer.
Definition: buffer.h:474
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:498
void writeData(const void *data, size_t len)
Copy an arbitrary length of data into the buffer.
Definition: buffer.h:556
void clear()
Clear buffer content.
Definition: buffer.h:467
@ D6O_INTERFACE_ID
Definition: dhcp6.h:38
@ D6O_CLIENTID
Definition: dhcp6.h:21
@ D6O_RELAY_MSG
Definition: dhcp6.h:29
@ D6O_REMOTE_ID
Definition: dhcp6.h:57
@ D6O_VENDOR_OPTS
Definition: dhcp6.h:37
@ D6O_RELAY_SOURCE_PORT
Definition: dhcp6.h:155
@ D6O_CLIENT_LINKLAYER_ADDR
Definition: dhcp6.h:99
@ DHCPV6_ADVERTISE
Definition: dhcp6.h:199
@ DHCPV6_LEASEQUERY
Definition: dhcp6.h:212
@ DHCPV6_REQUEST
Definition: dhcp6.h:200
@ DHCPV6_RELAY_REPL
Definition: dhcp6.h:210
@ DHCPV6_RENEW
Definition: dhcp6.h:202
@ DHCPV6_DHCPV4_QUERY
Definition: dhcp6.h:221
@ DHCPV6_DHCPV4_RESPONSE
Definition: dhcp6.h:222
@ DHCPV6_RECONFIGURE
Definition: dhcp6.h:207
@ DHCPV6_REBIND
Definition: dhcp6.h:203
@ DHCPV6_REPLY
Definition: dhcp6.h:204
@ DHCPV6_SOLICIT
Definition: dhcp6.h:198
@ DHCPV6_RELEASE
Definition: dhcp6.h:205
@ DHCPV6_LEASEQUERY_DATA
Definition: dhcp6.h:216
@ DHCPV6_INFORMATION_REQUEST
Definition: dhcp6.h:208
@ DHCPV6_CONFIRM
Definition: dhcp6.h:201
@ DHCPV6_LEASEQUERY_DONE
Definition: dhcp6.h:215
@ DHCPV6_LEASEQUERY_REPLY
Definition: dhcp6.h:213
@ DHCPV6_RELAY_FORW
Definition: dhcp6.h:209
@ DHCPV6_DECLINE
Definition: dhcp6.h:206
#define HOP_COUNT_LIMIT
Definition: dhcp6.h:314
#define VENDOR_ID_CABLE_LABS
#define DOCSIS3_V6_CMTS_CM_MAC
#define DOCSIS3_V6_DEVICE_ID
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
static const uint32_t HWADDR_SOURCE_REMOTE_ID
A relay can insert remote-id.
Definition: hwaddr.h:63
static const uint32_t HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION
Get it from RFC6939 option.
Definition: hwaddr.h:59
static const uint32_t HWADDR_SOURCE_DOCSIS_MODEM
A cable modem (acting as DHCP client) that supports DOCSIS standard can insert DOCSIS options that co...
Definition: hwaddr.h:79
static const uint32_t HWADDR_SOURCE_DUID
Extracted from DUID-LL or DUID-LLT (not 100% reliable as the client can send fake DUID).
Definition: hwaddr.h:48
static const uint32_t HWADDR_SOURCE_DOCSIS_CMTS
A CMTS (acting as DHCP relay agent) that supports DOCSIS standard can insert DOCSIS options that cont...
Definition: hwaddr.h:73
@ info
Definition: db_log.h: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