1 // Copyright (C) 2014-2020 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 #include <dhcp/dhcp6.h>
9 #include <dhcp/docsis3_option_defs.h>
10 #include <dhcp/option_custom.h>
11 #include <dhcp/option_int_array.h>
12 #include <dhcp/option_vendor.h>
13 #include <dhcp/option6_addrlst.h>
14 #include <dhcp/option6_ia.h>
15 #include <dhcp/option6_iaaddr.h>
16 #include <dhcp/option6_status_code.h>
17 #include <dhcp/pkt6.h>
18 #include <dhcp/tests/iface_mgr_test_config.h>
19 #include <dhcpsrv/lease.h>
20 #include <dhcpsrv/lease_mgr_factory.h>
21 #include <dhcpsrv/pool.h>
22 #include <dhcp6/tests/dhcp6_client.h>
23 #include <util/buffer.h>
24 #include <util/multi_threading_mgr.h>
25 #include <boost/foreach.hpp>
26 #include <boost/pointer_cast.hpp>
27 #include <algorithm>
28 #include <cstdlib>
29 #include <time.h>
30 #include <utility>
31
32 using namespace isc::asiolink;
33 using namespace isc::dhcp;
34 using namespace isc::dhcp::test;
35 using namespace isc::util;
36
37 namespace {
38
39 /// @brief Functor searching for the leases using a specified property.
40 ///
41 /// @tparam BaseType Base type to which the property belongs: @c Lease or
42 /// @c Lease6.
43 /// @tparam PropertyType A type of the property, e.g. @c uint32_t for IAID.
44 /// @tparam MemberPointer A pointer to the member, e.g. @c &Lease6::iaid_.
45 template<typename BaseType, typename PropertyType,
46 PropertyType BaseType::*MemberPointer>
47 struct getLeasesByPropertyFun {
48
49 /// @brief Returns leases matching the specified condition.
50 ///
51 /// @param config DHCP client configuration structure holding leases.
52 /// @param property A value of the lease property used to search the lease.
53 /// @param equals A flag which indicates if the operator should search for
54 /// the leases which property is equal to the value of @c property parameter
55 /// (if true), or unequal (if false).
56 /// @param [out] leases A vector in which the operator will store leases
57 /// found.
operator ()__anon397b8e880111::getLeasesByPropertyFun58 void operator()(const Dhcp6Client::Configuration& config,
59 const PropertyType& property, const bool equals,
60 std::vector<Lease6>& leases) {
61
62 // Iterate over the leases and match the property with a given lease
63 //field.
64 for (typename std::vector<Lease6>::const_iterator lease =
65 config.leases_.begin(); lease != config.leases_.end();
66 ++lease) {
67 // Check if fulfills the condition.
68 if ((equals && ((*lease).*MemberPointer) == property) ||
69 (!equals && ((*lease).*MemberPointer) != property)) {
70 // Found the matching lease.
71 leases.push_back(*lease);
72 }
73 }
74 }
75 };
76
77 /// @brief Returns leases which belong to specified pool.
78 ///
79 /// @param config DHCP client configuration structure holding leases.
80 /// @param pool Pool to which returned leases belong.
81 /// @param [out] leases A vector in which the function will store leases
82 /// found.
getLeasesByPool(const Dhcp6Client::Configuration & config,const Pool6 & pool,std::vector<Lease6> & leases)83 void getLeasesByPool(const Dhcp6Client::Configuration& config,
84 const Pool6& pool, std::vector<Lease6>& leases) {
85 for (std::vector<Lease6>::const_iterator lease =
86 config.leases_.begin(); lease != config.leases_.end();
87 ++lease) {
88 // Check if prefix in range.
89 if (pool.inRange(lease->addr_)) {
90 // Found the matching lease.
91 leases.push_back(*lease);
92 }
93 }
94 }
95
96 }; // end of anonymous namespace
97
98 namespace isc {
99 namespace dhcp {
100 namespace test {
101
Dhcp6Client()102 Dhcp6Client::Dhcp6Client() :
103 relay_link_addr_("3000:1::1"),
104 curr_transid_(0),
105 dest_addr_(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
106 duid_(generateDUID(DUID::DUID_LLT)),
107 link_local_("fe80::3a60:77ff:fed5:cdef"),
108 iface_name_("eth0"),
109 iface_index_(ETH0_INDEX),
110 srv_(boost::shared_ptr<NakedDhcpv6Srv>(new NakedDhcpv6Srv(0))),
111 use_relay_(false),
112 use_oro_(false),
113 use_docsis_oro_(false),
114 use_client_id_(true),
115 use_rapid_commit_(false),
116 client_ias_(),
117 fqdn_(),
118 interface_id_() {
119 }
120
Dhcp6Client(boost::shared_ptr<NakedDhcpv6Srv> & srv)121 Dhcp6Client::Dhcp6Client(boost::shared_ptr<NakedDhcpv6Srv>& srv) :
122 relay_link_addr_("3000:1::1"),
123 curr_transid_(0),
124 dest_addr_(ALL_DHCP_RELAY_AGENTS_AND_SERVERS),
125 duid_(generateDUID(DUID::DUID_LLT)),
126 link_local_("fe80::3a60:77ff:fed5:cdef"),
127 iface_name_("eth0"),
128 iface_index_(ETH0_INDEX),
129 srv_(srv),
130 use_relay_(false),
131 use_oro_(false),
132 use_docsis_oro_(false),
133 use_client_id_(true),
134 use_rapid_commit_(false),
135 client_ias_(),
136 fqdn_(),
137 interface_id_() {
138 }
139
140 void
applyRcvdConfiguration(const Pkt6Ptr & reply,uint32_t state)141 Dhcp6Client::applyRcvdConfiguration(const Pkt6Ptr& reply, uint32_t state) {
142 typedef OptionCollection Opts;
143 // Get all options in the reply message and pick IA_NA, IA_PD and
144 // Status code.
145 Opts opts = reply->options_;
146
147 // Let's try to get a MAC
148 HWAddrPtr hwaddr = reply->getMAC(HWAddr::HWADDR_SOURCE_ANY);
149
150 for (Opts::const_iterator opt = opts.begin(); opt != opts.end(); ++opt) {
151 Option6IAPtr ia = boost::dynamic_pointer_cast<Option6IA>(opt->second);
152 if (!ia) {
153 // This is not IA, so let's just store it.
154 config_.options_.insert(*opt);
155 continue;
156 }
157
158 const Opts& ia_opts = ia->getOptions();
159 for (Opts::const_iterator iter_ia_opt = ia_opts.begin();
160 iter_ia_opt != ia_opts.end(); ++iter_ia_opt) {
161 OptionPtr ia_opt = iter_ia_opt->second;
162 Lease6 lease;
163 lease.type_ = (ia->getType() == D6O_IA_NA ? Lease::TYPE_NA : Lease::TYPE_PD);
164 lease.iaid_ = ia->getIAID();
165
166 switch (ia_opt->getType()) {
167 case D6O_IAADDR:
168 {
169 Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast<
170 Option6IAAddr>(ia_opt);
171
172 if (iaaddr) {
173 lease = Lease6(Lease::TYPE_NA,
174 iaaddr->getAddress(),
175 duid_, ia->getIAID(),
176 iaaddr->getPreferred(),
177 iaaddr->getValid(), 0,
178 hwaddr);
179 lease.cltt_ = time(NULL);
180 lease.state_ = state;
181 applyLease(lease);
182 }
183 }
184 break;
185
186 case D6O_IAPREFIX:
187 {
188 Option6IAPrefixPtr iaprefix = boost::dynamic_pointer_cast<
189 Option6IAPrefix>(ia_opt);
190
191 if (iaprefix) {
192 lease = Lease6(Lease::TYPE_PD,
193 iaprefix->getAddress(), duid_,
194 ia->getIAID(),
195 iaprefix->getPreferred(),
196 iaprefix->getValid(), 0,
197 hwaddr,
198 iaprefix->getLength());
199 lease.cltt_ = time(NULL);
200 applyLease(lease);
201 }
202 }
203 break;
204
205 case D6O_STATUS_CODE:
206 {
207 // Check if the server has sent status code. If no status
208 // code, assume the status code to be 0.
209 Option6StatusCodePtr status_code = boost::dynamic_pointer_cast<
210 Option6StatusCode>(ia->getOption(D6O_STATUS_CODE));
211 config_.status_codes_[ia->getIAID()] =
212 (status_code ? status_code->getStatusCode() : 0);
213 }
214 break;
215
216 default:
217 ; // no-op
218 }
219
220 }
221 }
222
223 // Get the global status code.
224 Option6StatusCodePtr status_code = boost::dynamic_pointer_cast<
225 Option6StatusCode>(reply->getOption(D6O_STATUS_CODE));
226 // If status code has been sent, we override the default status code:
227 // Success and record that we have received the status code.
228 if (status_code) {
229 config_.received_status_code_ = true;
230 config_.status_code_ = status_code->getStatusCode();
231 }
232 }
233
234 void
applyLease(const Lease6 & lease)235 Dhcp6Client::applyLease(const Lease6& lease) {
236 // Go over existing leases and try to match the one that we have.
237 for (size_t i = 0; i < config_.leases_.size(); ++i) {
238 Lease6 existing_lease = config_.leases_[i];
239 // If IAID is matching and there is an actual address assigned
240 // replace the current lease. The default address is :: if the
241 // server hasn't sent the IA option. In this case, there is no
242 // lease assignment so we keep what we have.
243 if ((existing_lease.iaid_ == lease.iaid_)
244 && (existing_lease.type_ == lease.type_)
245 && (lease.addr_ != asiolink::IOAddress("::"))
246 && (existing_lease.addr_ == lease.addr_)) {
247 config_.leases_[i] = lease;
248 return;
249 }
250 }
251
252 // It is a new lease. Add it.
253 config_.leases_.push_back(lease);
254 }
255
256 void
appendFQDN()257 Dhcp6Client::appendFQDN() {
258 if (fqdn_) {
259 context_.query_->addOption(fqdn_);
260 }
261 }
262
263 void
appendRequestedIAs(const Pkt6Ptr & query) const264 Dhcp6Client::appendRequestedIAs(const Pkt6Ptr& query) const {
265 BOOST_FOREACH(const ClientIA& ia, client_ias_) {
266 OptionCollection options =
267 query->getOptions(ia.type_ == Lease::TYPE_NA ?
268 D6O_IA_NA : D6O_IA_PD);
269 std::pair<unsigned int, OptionPtr> option_pair;
270 Option6IAPtr existing_ia;
271 BOOST_FOREACH(option_pair, options) {
272 Option6IAPtr ia_opt =
273 boost::dynamic_pointer_cast<Option6IA>(option_pair.second);
274 // This shouldn't happen.
275 if (!ia_opt) {
276 isc_throw(Unexpected,
277 "Dhcp6Client: IA option has an invalid C++ type;"
278 " this is a programming issue");
279 }
280 if (ia_opt->getIAID() == ia.iaid_) {
281 existing_ia = ia_opt;
282 }
283 }
284 if (!existing_ia) {
285 existing_ia.reset(new Option6IA(ia.type_ == Lease::TYPE_NA ?
286 D6O_IA_NA : D6O_IA_PD, ia.iaid_));
287 query->addOption(existing_ia);
288 }
289
290 bool option_exists = false;
291 if ((ia.type_ == Lease::TYPE_NA) && !ia.prefix_.isV6Zero()) {
292 Option6IAAddrPtr ia_addr(new Option6IAAddr(D6O_IAADDR, ia.prefix_,
293 0, 0));
294 BOOST_FOREACH(option_pair, existing_ia->getOptions()) {
295 Option6IAAddrPtr existing_addr = boost::dynamic_pointer_cast<
296 Option6IAAddr>(option_pair.second);
297 if (existing_addr &&
298 (existing_addr->getAddress() == ia.prefix_)) {
299 option_exists = true;
300 }
301 }
302
303 if (!option_exists) {
304 existing_ia->addOption(ia_addr);
305 }
306
307 } else if ((ia.type_ == Lease::TYPE_PD) &&
308 (!ia.prefix_.isV6Zero() || (ia.prefix_len_ > 0))) {
309 Option6IAPrefixPtr ia_prefix(new Option6IAPrefix(D6O_IAPREFIX,
310 ia.prefix_,
311 ia.prefix_len_,
312 0, 0));
313 BOOST_FOREACH(option_pair, existing_ia->getOptions()) {
314 Option6IAPrefixPtr existing_prefix =
315 boost::dynamic_pointer_cast<Option6IAPrefix>(option_pair.second);
316 if (existing_prefix &&
317 (existing_prefix->getAddress() == ia.prefix_) &&
318 existing_prefix->getLength()) {
319 option_exists = true;
320 }
321 }
322
323 if (!option_exists) {
324 existing_ia->addOption(ia_prefix);
325 }
326 }
327 }
328 }
329
330 void
copyIAs(const Pkt6Ptr & source,const Pkt6Ptr & dest)331 Dhcp6Client::copyIAs(const Pkt6Ptr& source, const Pkt6Ptr& dest) {
332 typedef OptionCollection Opts;
333 // Copy IA_NAs.
334 Opts opts = source->getOptions(D6O_IA_NA);
335 for (Opts::const_iterator opt = opts.begin(); opt != opts.end(); ++opt) {
336 // Only copy the entire IA_NA if there is at lease one IA Address option.
337 if (opt->second->getOption(D6O_IAADDR)) {
338 dest->addOption(opt->second);
339 }
340 }
341 // Copy IA_PDs.
342 opts = source->getOptions(D6O_IA_PD);
343 for (Opts::const_iterator opt = opts.begin(); opt != opts.end(); ++opt) {
344 // Only copy the entire IA_PD if there is at least one IA Prefix option
345 // in it.
346 if (opt->second->getOption(D6O_IAPREFIX)) {
347 dest->addOption(opt->second);
348 }
349 }
350 }
351
352 void
copyIAsFromLeases(const Pkt6Ptr & dest) const353 Dhcp6Client::copyIAsFromLeases(const Pkt6Ptr& dest) const {
354 // Go over leases and create IA_NA and IA_PD options from them.
355 // Create one IA per lease.
356 std::set<uint32_t> iaids = getIAIDs();
357 for (std::set<uint32_t>::const_iterator iaid = iaids.begin();
358 iaid != iaids.end(); ++iaid) {
359 std::vector<Lease6> leases = getLeasesByIAID(*iaid);
360 // Only a valid lease should be included. Do not copy a
361 // lease which have been marked by the server as invalid.
362 if (leases[0].valid_lft_ == 0) {
363 continue;
364 }
365 Option6IAPtr opt(new Option6IA(leases[0].type_ == Lease::TYPE_NA ?
366 D6O_IA_NA : D6O_IA_PD, *iaid));
367 for (std::vector<Lease6>::const_iterator lease = leases.begin();
368 lease != leases.end(); ++lease) {
369 if ((lease->preferred_lft_ != 0) && (lease->valid_lft_ != 0)) {
370 if (lease->type_ == Lease::TYPE_NA) {
371 opt->addOption(Option6IAAddrPtr(new Option6IAAddr(
372 D6O_IAADDR,
373 lease->addr_,
374 lease->preferred_lft_,
375 lease->valid_lft_)));
376 } else if (lease->type_ == Lease::TYPE_PD) {
377 opt->addOption(Option6IAAddrPtr(new Option6IAPrefix(
378 D6O_IAPREFIX,
379 lease->addr_,
380 lease->prefixlen_,
381 lease->preferred_lft_,
382 lease->valid_lft_)));
383 }
384 }
385 }
386 dest->addOption(opt);
387 }
388 }
389
390 void
createLease(const Lease6 & lease)391 Dhcp6Client::createLease(const Lease6& lease) {
392 applyLease(lease);
393 }
394
395 Pkt6Ptr
createMsg(const uint8_t msg_type)396 Dhcp6Client::createMsg(const uint8_t msg_type) {
397 Pkt6Ptr msg(new Pkt6(msg_type, curr_transid_++));
398
399 if (use_client_id_) {
400 msg->addOption(getClientId());
401 }
402
403 if (use_oro_) {
404 OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6, D6O_ORO));
405 oro->setValues(oro_);
406
407 msg->addOption(oro);
408 };
409
410 if (use_docsis_oro_) {
411 OptionUint16ArrayPtr vendor_oro(new OptionUint16Array(Option::V6,
412 DOCSIS3_V6_ORO));
413 vendor_oro->setValues(docsis_oro_);
414 OptionPtr vendor(new OptionVendor(Option::V6, 4491));
415 vendor->addOption(vendor_oro);
416 msg->addOption(vendor);
417 }
418
419 // If there are any custom options specified, add them all to the message.
420 if (!extra_options_.empty()) {
421 for (OptionCollection::iterator opt = extra_options_.begin();
422 opt != extra_options_.end(); ++opt) {
423 msg->addOption(opt->second);
424 }
425 }
426
427 // Add classes.
428 for (ClientClasses::const_iterator cclass = classes_.cbegin();
429 cclass != classes_.cend(); ++cclass) {
430 msg->addClass(*cclass);
431 }
432
433 return (msg);
434 }
435
436 void
doSARR()437 Dhcp6Client::doSARR() {
438 doSolicit();
439 // Don't send the Request if there was no Advertise.
440 if (context_.response_) {
441 doRequest();
442 }
443 }
444
445 void
doSolicit(const bool always_apply_config)446 Dhcp6Client::doSolicit(const bool always_apply_config) {
447 context_.query_ = createMsg(DHCPV6_SOLICIT);
448 if (forced_server_id_) {
449 context_.query_->addOption(forced_server_id_);
450 }
451
452 // Append requested (empty) IAs.
453 appendRequestedIAs(context_.query_);
454
455 if (use_rapid_commit_) {
456 context_.query_->addOption(OptionPtr(new Option(Option::V6,
457 D6O_RAPID_COMMIT)));
458 }
459 // Add Client FQDN if configured.
460 appendFQDN();
461
462 sendMsg(context_.query_);
463 context_.response_ = receiveOneMsg();
464
465 // If using Rapid Commit and the server has responded with Reply,
466 // let's apply received configuration. We also apply the configuration
467 // for the Advertise if instructed to do so.
468 if (context_.response_ &&
469 (always_apply_config ||
470 (use_rapid_commit_ &&
471 context_.response_->getType() == DHCPV6_REPLY))) {
472 config_.clear();
473 applyRcvdConfiguration(context_.response_);
474 }
475 }
476
477 void
doRequest()478 Dhcp6Client::doRequest() {
479 Pkt6Ptr query = createMsg(DHCPV6_REQUEST);
480 if (!forced_server_id_) {
481 query->addOption(context_.response_->getOption(D6O_SERVERID));
482 } else {
483 query->addOption(forced_server_id_);
484 }
485 copyIAs(context_.response_, query);
486 appendRequestedIAs(query);
487
488 context_.query_ = query;
489
490 // Add Client FQDN if configured.
491 appendFQDN();
492
493 sendMsg(context_.query_);
494 context_.response_ = receiveOneMsg();
495
496 /// @todo sanity check here.
497
498 // Apply new configuration only if the server has responded.
499 if (context_.response_) {
500 config_.clear();
501 applyRcvdConfiguration(context_.response_);
502 }
503 }
504
505 void
doInfRequest()506 Dhcp6Client::doInfRequest() {
507 context_.query_ = createMsg(DHCPV6_INFORMATION_REQUEST);
508
509 // IA_NA, IA_TA and IA_PD options are not allowed in INF-REQUEST,
510 // but hey! Let's test it.
511 appendRequestedIAs(context_.query_);
512
513 sendMsg(context_.query_);
514 context_.response_ = receiveOneMsg();
515 // Apply new configuration only if the server has responded.
516 if (context_.response_) {
517 config_.clear();
518 applyRcvdConfiguration(context_.response_);
519 }
520 }
521
522 void
doRenew()523 Dhcp6Client::doRenew() {
524 Pkt6Ptr query = createMsg(DHCPV6_RENEW);
525 query->addOption(context_.response_->getOption(D6O_SERVERID));
526 copyIAsFromLeases(query);
527
528 // During the Renew the client may request additional bindings per
529 // RFC 8415.
530 appendRequestedIAs(query);
531
532 context_.query_ = query;
533
534 // Add Client FQDN if configured.
535 appendFQDN();
536
537 sendMsg(context_.query_);
538 context_.response_ = receiveOneMsg();
539
540 // Apply configuration only if the server has responded.
541 if (context_.response_) {
542 config_.clear();
543 applyRcvdConfiguration(context_.response_);
544 }
545 }
546
547 void
doRebind()548 Dhcp6Client::doRebind() {
549 Pkt6Ptr query = createMsg(DHCPV6_REBIND);
550 copyIAsFromLeases(query);
551
552 // During the Rebind the client may request additional bindings per
553 // RFC 8415.
554 appendRequestedIAs(query);
555
556 // Add Client FQDN if configured.
557 appendFQDN();
558
559 context_.query_ = query;
560 sendMsg(context_.query_);
561 context_.response_ = receiveOneMsg();
562 // Apply configuration only if the server has responded.
563 if (context_.response_) {
564 config_.clear();
565 applyRcvdConfiguration(context_.response_);
566 }
567 }
568
569 void
doConfirm()570 Dhcp6Client::doConfirm() {
571 context_.query_ = createMsg(DHCPV6_CONFIRM);
572 copyIAsFromLeases(context_.query_);
573 sendMsg(context_.query_);
574 context_.response_ = receiveOneMsg();
575 // Set the global status code to default: success and not received.
576 config_.resetGlobalStatusCode();
577 if (context_.response_) {
578 config_.options_.clear();
579 applyRcvdConfiguration(context_.response_);
580 }
581 }
582
583 void
doDecline(const bool include_address)584 Dhcp6Client::doDecline(const bool include_address) {
585 Pkt6Ptr query = createMsg(DHCPV6_DECLINE);
586 if (!forced_server_id_) {
587 query->addOption(context_.response_->getOption(D6O_SERVERID));
588 } else {
589 query->addOption(forced_server_id_);
590 }
591
592 generateIAFromLeases(query, include_address);
593
594 context_.query_ = query;
595 sendMsg(context_.query_);
596 context_.response_ = receiveOneMsg();
597
598 // Apply new configuration only if the server has responded.
599 if (context_.response_) {
600 config_.clear();
601 applyRcvdConfiguration(context_.response_);
602 }
603 }
604
605 void
doRelease()606 Dhcp6Client::doRelease() {
607 Pkt6Ptr query = createMsg(DHCPV6_RELEASE);
608 query->addOption(context_.response_->getOption(D6O_SERVERID));
609 copyIAsFromLeases(query);
610
611 context_.query_ = query;
612
613 sendMsg(context_.query_);
614 context_.response_ = receiveOneMsg();
615
616 // Apply configuration only if the server has responded.
617 if (context_.response_) {
618 config_.clear();
619 applyRcvdConfiguration(context_.response_);
620 }
621 }
622
623 void
generateIAFromLeases(const Pkt6Ptr & query,const bool include_address)624 Dhcp6Client::generateIAFromLeases(const Pkt6Ptr& query,
625 const bool include_address) {
626 /// @todo: add support for IAPREFIX here.
627
628 for (std::vector<Lease6>::const_iterator lease = config_.leases_.begin();
629 lease != config_.leases_.end(); ++lease) {
630 if (lease->type_ != Lease::TYPE_NA) {
631 continue;
632 }
633
634 Option6IAPtr ia(new Option6IA(D6O_IA_NA, lease->iaid_));
635
636 if (include_address) {
637 ia->addOption(OptionPtr(new Option6IAAddr(D6O_IAADDR,
638 lease->addr_, lease->preferred_lft_, lease->valid_lft_)));
639 }
640 query->addOption(ia);
641 }
642 }
643
644 void
fastFwdTime(const uint32_t secs,const bool update_server)645 Dhcp6Client::fastFwdTime(const uint32_t secs, const bool update_server) {
646 // Iterate over all leases and move their cltt backwards.
647 for (size_t i = 0; i < config_.leases_.size(); ++i) {
648 config_.leases_[i].cltt_ -= secs;
649 if (update_server) {
650 Lease6Ptr lease(new Lease6(config_.leases_[i]));
651 LeaseMgrFactory::instance().updateLease6(lease);
652 }
653 }
654 }
655
656 DuidPtr
generateDUID(DUID::DUIDType duid_type) const657 Dhcp6Client::generateDUID(DUID::DUIDType duid_type) const {
658 std::vector<uint8_t> duid;
659
660 /// @todo remove this check once other DUID types are implemented.
661 if (duid_type != DUID::DUID_LLT) {
662 isc_throw(NotImplemented, "currently the Dhcp6Client only supports"
663 " generation of DUID LLT");
664 }
665 duid.push_back(static_cast<uint8_t>(duid_type));
666 for (int i = 0; i < 4; ++i) {
667 duid.push_back(static_cast<uint8_t>(rand() % 255));
668 }
669 for (int i = 0; i < 6; ++i) {
670 duid.push_back(static_cast<uint8_t>(i));
671 }
672
673 return (DuidPtr(new DUID(duid)));
674 }
675
676 OptionPtr
getClientId() const677 Dhcp6Client::getClientId() const {
678 OptionPtr opt_client_id(new Option(Option::V6,
679 D6O_CLIENTID,
680 duid_->getDuid().begin(),
681 duid_->getDuid().end()));
682 return (opt_client_id);
683 }
684
685 std::set<uint32_t>
getIAIDs() const686 Dhcp6Client::getIAIDs() const {
687 std::set<uint32_t> iaids;
688 for (std::vector<Lease6>::const_iterator lease = config_.leases_.begin();
689 lease != config_.leases_.end(); ++lease) {
690 iaids.insert(lease->iaid_);
691 }
692 return (iaids);
693 }
694
695 std::vector<Lease6>
getLeasesByIAID(const uint32_t iaid) const696 Dhcp6Client::getLeasesByIAID(const uint32_t iaid) const {
697 std::vector<Lease6> leases;
698 getLeasesByProperty<Lease6, uint32_t, &Lease6::iaid_>(iaid, true, leases);
699 return (leases);
700 }
701
702 template<typename BaseType, typename PropertyType, PropertyType BaseType::*MemberPointer>
703 void
getLeasesByProperty(const PropertyType & property,const bool equals,std::vector<Lease6> & leases) const704 Dhcp6Client::getLeasesByProperty(const PropertyType& property, const bool equals,
705 std::vector<Lease6>& leases) const {
706 getLeasesByPropertyFun<BaseType, PropertyType, MemberPointer> fun;
707 fun(config_, property, equals, leases);
708 }
709
710 std::vector<Lease6>
getLeasesByType(const Lease6::Type & lease_type) const711 Dhcp6Client::getLeasesByType(const Lease6::Type& lease_type) const {
712 std::vector<Lease6> leases;
713 getLeasesByProperty<Lease6, Lease6::Type, &Lease6::type_>(lease_type, true, leases);
714 return (leases);
715 }
716
717 std::vector<Lease6>
getLeasesWithNonZeroLifetime() const718 Dhcp6Client::getLeasesWithNonZeroLifetime() const {
719 std::vector<Lease6> leases;
720 getLeasesByProperty<Lease, uint32_t, &Lease::valid_lft_>(0, false, leases);
721 return (leases);
722 }
723
724 std::vector<Lease6>
getLeasesWithZeroLifetime() const725 Dhcp6Client::getLeasesWithZeroLifetime() const {
726 std::vector<Lease6> leases;
727 getLeasesByProperty<Lease, uint32_t, &Lease::valid_lft_>(0, true, leases);
728 return (leases);
729 }
730
731 std::vector<Lease6>
getLeasesByAddress(const IOAddress & address) const732 Dhcp6Client::getLeasesByAddress(const IOAddress& address) const {
733 std::vector<Lease6> leases;
734 getLeasesByProperty<Lease, IOAddress, &Lease::addr_>(address, true, leases);
735 return (leases);
736 }
737
738 std::vector<Lease6>
getLeasesByAddressRange(const IOAddress & first,const IOAddress & second) const739 Dhcp6Client::getLeasesByAddressRange(const IOAddress& first,
740 const IOAddress& second) const {
741 std::vector<Lease6> leases;
742 getLeasesByPool(config_, Pool6(Lease::TYPE_NA, first, second), leases);
743 return (leases);
744 }
745
746 std::vector<Lease6>
getLeasesByPrefixPool(const asiolink::IOAddress & prefix,const uint8_t prefix_len,const uint8_t delegated_len) const747 Dhcp6Client::getLeasesByPrefixPool(const asiolink::IOAddress& prefix,
748 const uint8_t prefix_len,
749 const uint8_t delegated_len) const {
750 std::vector<Lease6> leases;
751 getLeasesByPool(config_, Pool6(Lease::TYPE_PD, prefix, prefix_len,
752 delegated_len), leases);
753 return (leases);
754 }
755
756 bool
hasLeaseForAddress(const asiolink::IOAddress & address) const757 Dhcp6Client::hasLeaseForAddress(const asiolink::IOAddress& address) const {
758 std::vector<Lease6> leases = getLeasesByAddress(address);
759 return (!leases.empty());
760 }
761
762 bool
hasLeaseForAddress(const asiolink::IOAddress & address,const IAID & iaid) const763 Dhcp6Client::hasLeaseForAddress(const asiolink::IOAddress& address,
764 const IAID& iaid) const {
765 std::vector<Lease6> leases = getLeasesByAddress(address);
766 BOOST_FOREACH(const Lease6& lease, leases) {
767 if (lease.iaid_ == iaid) {
768 return (true);
769 }
770 }
771 return (false);
772 }
773
774 bool
hasLeaseForAddressRange(const asiolink::IOAddress & first,const asiolink::IOAddress & last) const775 Dhcp6Client::hasLeaseForAddressRange(const asiolink::IOAddress& first,
776 const asiolink::IOAddress& last) const {
777 std::vector<Lease6> leases = getLeasesByAddressRange(first, last);
778 return (!leases.empty());
779 }
780
781 bool
hasLeaseWithZeroLifetimeForAddress(const asiolink::IOAddress & address) const782 Dhcp6Client::hasLeaseWithZeroLifetimeForAddress(const asiolink::IOAddress& address) const {
783 std::vector<Lease6> leases = getLeasesByAddress(address);
784 BOOST_FOREACH(const Lease6& lease, leases) {
785 if ((lease.preferred_lft_ == 0) && (lease.valid_lft_ == 0)) {
786 return (true);
787 }
788 }
789 return (false);
790 }
791
792
793 bool
hasLeaseForPrefix(const asiolink::IOAddress & prefix,const uint8_t prefix_len) const794 Dhcp6Client::hasLeaseForPrefix(const asiolink::IOAddress& prefix,
795 const uint8_t prefix_len) const {
796 std::vector<Lease6> leases = getLeasesByAddress(prefix);
797 BOOST_FOREACH(const Lease6& lease, leases) {
798 if (lease.prefixlen_ == prefix_len) {
799 return (true);
800 }
801 }
802 return (false);
803 }
804
805 bool
hasLeaseForPrefix(const asiolink::IOAddress & prefix,const uint8_t prefix_len,const IAID & iaid) const806 Dhcp6Client::hasLeaseForPrefix(const asiolink::IOAddress& prefix,
807 const uint8_t prefix_len,
808 const IAID& iaid) const {
809 std::vector<Lease6> leases = getLeasesByAddress(prefix);
810 BOOST_FOREACH(const Lease6& lease, leases) {
811 if ((lease.prefixlen_ == prefix_len) &&
812 (lease.iaid_ == iaid)) {
813 return (true);
814 }
815 }
816 return (false);
817 }
818
819 bool
hasLeaseForPrefixPool(const asiolink::IOAddress & prefix,const uint8_t prefix_len,const uint8_t delegated_len) const820 Dhcp6Client::hasLeaseForPrefixPool(const asiolink::IOAddress& prefix,
821 const uint8_t prefix_len,
822 const uint8_t delegated_len) const {
823 std::vector<Lease6> leases = getLeasesByPrefixPool(prefix, prefix_len,
824 delegated_len);
825 return (!leases.empty());
826 }
827
828 bool
hasLeaseWithZeroLifetimeForPrefix(const asiolink::IOAddress & prefix,const uint8_t prefix_len) const829 Dhcp6Client::hasLeaseWithZeroLifetimeForPrefix(const asiolink::IOAddress& prefix,
830 const uint8_t prefix_len) const {
831 std::vector<Lease6> leases = getLeasesByAddress(prefix);
832 BOOST_FOREACH(const Lease6& lease, leases) {
833 if ((lease.prefixlen_ == prefix_len) && (lease.preferred_lft_ == 0) &&
834 (lease.valid_lft_ == 0)) {
835 return (true);
836 }
837 }
838 return (false);
839 }
840
841 bool
hasOptionWithAddress(const uint16_t option_type,const std::string & expected_address) const842 Dhcp6Client::hasOptionWithAddress(const uint16_t option_type,
843 const std::string& expected_address) const {
844 Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
845 Option6AddrLst>(config_.findOption(option_type));
846 if (opt) {
847 Option6AddrLst::AddressContainer addrs = opt->getAddresses();
848 if (!addrs.empty()) {
849 return (std::find(addrs.begin(), addrs.end(),
850 IOAddress(expected_address)) != addrs.end());
851 }
852 }
853 return (false);
854 }
855
856 uint16_t
getStatusCode(const uint32_t iaid) const857 Dhcp6Client::getStatusCode(const uint32_t iaid) const {
858 std::map<uint32_t, uint16_t>::const_iterator status_code =
859 config_.status_codes_.find(iaid);
860 if (status_code == config_.status_codes_.end()) {
861 if (!getLeasesByIAID(iaid).empty()) {
862 return (STATUS_Success);
863 }
864
865 } else {
866 return (status_code->second);
867 }
868
869 return (0xFFFF);
870 }
871
872 bool
getTeeTimes(const uint32_t iaid,uint32_t & t1,uint32_t & t2) const873 Dhcp6Client::getTeeTimes(const uint32_t iaid, uint32_t& t1, uint32_t& t2) const {
874 // Sanity check.
875 if (!context_.response_) {
876 return (false);
877 }
878
879 // Get all options in the response message and pick IA_NA, IA_PD.
880 OptionCollection opts = context_.response_->options_;
881
882 for (auto opt = opts.begin(); opt != opts.end(); ++opt) {
883 Option6IAPtr ia = boost::dynamic_pointer_cast<Option6IA>(opt->second);
884 if (!ia) {
885 // This is not IA, so let's just skip it.
886 continue;
887 }
888 if (ia->getIAID() != iaid) {
889 // This is not the right IA, so let's just skip it.
890 continue;
891 }
892 // Found the IA.
893 t1 = ia->getT1();
894 t2 = ia->getT2();
895 return (true);
896 }
897
898 // Not found the IA.
899 return (false);
900 }
901
902 void
setDUID(const std::string & str)903 Dhcp6Client::setDUID(const std::string& str) {
904 DUID d = DUID::fromText(str);
905 duid_.reset(new DUID(d));
906 }
907
908 void
modifyDUID()909 Dhcp6Client::modifyDUID() {
910 if (!duid_) {
911 duid_ = generateDUID(DUID::DUID_LLT);
912 return;
913 }
914 std::vector<uint8_t> new_duid = duid_->getDuid();
915 // Modify the DUID by adding 1 to its last byte.
916 ++new_duid[new_duid.size() - 1];
917 duid_.reset(new DUID(new_duid));
918 }
919
920 Pkt6Ptr
receiveOneMsg()921 Dhcp6Client::receiveOneMsg() {
922 return (srv_->receiveOneMsg());
923 }
924
925 void
receiveResponse()926 Dhcp6Client::receiveResponse() {
927 context_.response_ = receiveOneMsg();
928 // If the server has responded, store the configuration received.
929 if (context_.response_) {
930 config_.clear();
931 applyRcvdConfiguration(context_.response_);
932 }
933 }
934
935 void
sendMsg(const Pkt6Ptr & msg)936 Dhcp6Client::sendMsg(const Pkt6Ptr& msg) {
937 srv_->shutdown_ = false;
938 // The client is configured to send through the relay. We achieve that
939 // adding the relay structure.
940 if (use_relay_ || !relay_info_.empty()) {
941 if (relay_info_.empty()) {
942 // Let's craft the relay info by hand
943 Pkt6::RelayInfo relay;
944 relay.linkaddr_ = relay_link_addr_;
945 relay.peeraddr_ = asiolink::IOAddress("fe80::1");
946 relay.msg_type_ = DHCPV6_RELAY_FORW;
947 relay.hop_count_ = 1;
948
949 // Interface identifier, if specified.
950 if (interface_id_) {
951 relay.options_.insert(std::make_pair(D6O_INTERFACE_ID, interface_id_));
952 }
953
954 msg->relay_info_.push_back(relay);
955
956 } else {
957 // The test provided relay_info_, let's use that.
958 msg->relay_info_ = relay_info_;
959 }
960 }
961 // Repack the message to simulate wire-data parsing.
962 msg->pack();
963
964 Pkt6Ptr msg_copy(new Pkt6(static_cast<const uint8_t*>
965 (msg->getBuffer().getData()),
966 msg->getBuffer().getLength()));
967 msg_copy->setRemoteAddr(link_local_);
968 msg_copy->setLocalAddr(dest_addr_);
969 msg_copy->setIface(iface_name_);
970 msg_copy->setIndex(iface_index_);
971
972 // Copy classes
973 const ClientClasses& classes = msg->getClasses();
974 for (ClientClasses::const_iterator cclass = classes.cbegin();
975 cclass != classes.cend(); ++cclass) {
976 msg_copy->addClass(*cclass);
977 }
978
979 srv_->fakeReceive(msg_copy);
980
981 try {
982 // Invoke run_one instead of run, because we want to avoid triggering
983 // IO service.
984 srv_->run_one();
985 } catch (...) {
986 // Suppress errors, as the DHCPv6 server does.
987 }
988
989 // Make sure the server processed all packets in MT.
990 isc::util::MultiThreadingMgr::instance().getThreadPool().wait(3);
991 }
992
993 void
requestAddress(const uint32_t iaid,const asiolink::IOAddress & address)994 Dhcp6Client::requestAddress(const uint32_t iaid,
995 const asiolink::IOAddress& address) {
996 client_ias_.push_back(ClientIA(Lease::TYPE_NA, iaid, address, 128));
997 }
998
999 void
requestPrefix(const uint32_t iaid,const uint8_t prefix_len,const asiolink::IOAddress & prefix)1000 Dhcp6Client::requestPrefix(const uint32_t iaid,
1001 const uint8_t prefix_len,
1002 const asiolink::IOAddress& prefix) {
1003 client_ias_.push_back(ClientIA(Lease::TYPE_PD, iaid, prefix, prefix_len));
1004 }
1005
1006 void
useInterfaceId(const std::string & interface_id)1007 Dhcp6Client::useInterfaceId(const std::string& interface_id) {
1008 OptionBuffer buf(interface_id.begin(), interface_id.end());
1009 interface_id_.reset(new Option(Option::V6, D6O_INTERFACE_ID, buf));
1010 }
1011
1012 void
useFQDN(const uint8_t flags,const std::string & fqdn_name,Option6ClientFqdn::DomainNameType fqdn_type)1013 Dhcp6Client::useFQDN(const uint8_t flags, const std::string& fqdn_name,
1014 Option6ClientFqdn::DomainNameType fqdn_type) {
1015 fqdn_.reset(new Option6ClientFqdn(flags, fqdn_name, fqdn_type));
1016 }
1017
1018 void
addExtraOption(const OptionPtr & opt)1019 Dhcp6Client::addExtraOption(const OptionPtr& opt) {
1020 extra_options_.insert(std::make_pair(opt->getType(), opt));
1021 }
1022
1023 void
clearExtraOptions()1024 Dhcp6Client::clearExtraOptions() {
1025 extra_options_.clear();
1026 }
1027
1028 void
addClass(const ClientClass & client_class)1029 Dhcp6Client::addClass(const ClientClass& client_class) {
1030 if (!classes_.contains(client_class)) {
1031 classes_.insert(client_class);
1032 }
1033 }
1034
1035 void
clearClasses()1036 Dhcp6Client::clearClasses() {
1037 classes_.clear();
1038 }
1039
1040 void
printConfiguration() const1041 Dhcp6Client::printConfiguration() const {
1042
1043 // Print DUID
1044 std::cout << "Client " << (duid_ ? duid_->toText() : "(without duid)")
1045 << " got " << getLeaseNum() << " lease(s): ";
1046
1047 // Print leases
1048 for (int i = 0; i < getLeaseNum(); i++) {
1049 Lease6 lease = getLease(i);
1050 std::cout << lease.addr_.toText() << " ";
1051 }
1052
1053 /// @todo: Print many other parameters.
1054 std::cout << std::endl;
1055 }
1056
1057 } // end of namespace isc::dhcp::test
1058 } // end of namespace isc::dhcp
1059 } // end of namespace isc
1060