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