// Copyright (C) 2014-2020 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace isc::asiolink; using namespace isc::dhcp; using namespace isc::dhcp::test; using namespace isc::util; namespace { /// @brief Functor searching for the leases using a specified property. /// /// @tparam BaseType Base type to which the property belongs: @c Lease or /// @c Lease6. /// @tparam PropertyType A type of the property, e.g. @c uint32_t for IAID. /// @tparam MemberPointer A pointer to the member, e.g. @c &Lease6::iaid_. template struct getLeasesByPropertyFun { /// @brief Returns leases matching the specified condition. /// /// @param config DHCP client configuration structure holding leases. /// @param property A value of the lease property used to search the lease. /// @param equals A flag which indicates if the operator should search for /// the leases which property is equal to the value of @c property parameter /// (if true), or unequal (if false). /// @param [out] leases A vector in which the operator will store leases /// found. void operator()(const Dhcp6Client::Configuration& config, const PropertyType& property, const bool equals, std::vector& leases) { // Iterate over the leases and match the property with a given lease //field. for (typename std::vector::const_iterator lease = config.leases_.begin(); lease != config.leases_.end(); ++lease) { // Check if fulfills the condition. if ((equals && ((*lease).*MemberPointer) == property) || (!equals && ((*lease).*MemberPointer) != property)) { // Found the matching lease. leases.push_back(*lease); } } } }; /// @brief Returns leases which belong to specified pool. /// /// @param config DHCP client configuration structure holding leases. /// @param pool Pool to which returned leases belong. /// @param [out] leases A vector in which the function will store leases /// found. void getLeasesByPool(const Dhcp6Client::Configuration& config, const Pool6& pool, std::vector& leases) { for (std::vector::const_iterator lease = config.leases_.begin(); lease != config.leases_.end(); ++lease) { // Check if prefix in range. if (pool.inRange(lease->addr_)) { // Found the matching lease. leases.push_back(*lease); } } } }; // end of anonymous namespace namespace isc { namespace dhcp { namespace test { Dhcp6Client::Dhcp6Client() : relay_link_addr_("3000:1::1"), curr_transid_(0), dest_addr_(ALL_DHCP_RELAY_AGENTS_AND_SERVERS), duid_(generateDUID(DUID::DUID_LLT)), link_local_("fe80::3a60:77ff:fed5:cdef"), iface_name_("eth0"), iface_index_(ETH0_INDEX), srv_(boost::shared_ptr(new NakedDhcpv6Srv(0))), use_relay_(false), use_oro_(false), use_docsis_oro_(false), use_client_id_(true), use_rapid_commit_(false), client_ias_(), fqdn_(), interface_id_() { } Dhcp6Client::Dhcp6Client(boost::shared_ptr& srv) : relay_link_addr_("3000:1::1"), curr_transid_(0), dest_addr_(ALL_DHCP_RELAY_AGENTS_AND_SERVERS), duid_(generateDUID(DUID::DUID_LLT)), link_local_("fe80::3a60:77ff:fed5:cdef"), iface_name_("eth0"), iface_index_(ETH0_INDEX), srv_(srv), use_relay_(false), use_oro_(false), use_docsis_oro_(false), use_client_id_(true), use_rapid_commit_(false), client_ias_(), fqdn_(), interface_id_() { } void Dhcp6Client::applyRcvdConfiguration(const Pkt6Ptr& reply, uint32_t state) { typedef OptionCollection Opts; // Get all options in the reply message and pick IA_NA, IA_PD and // Status code. Opts opts = reply->options_; // Let's try to get a MAC HWAddrPtr hwaddr = reply->getMAC(HWAddr::HWADDR_SOURCE_ANY); for (Opts::const_iterator opt = opts.begin(); opt != opts.end(); ++opt) { Option6IAPtr ia = boost::dynamic_pointer_cast(opt->second); if (!ia) { // This is not IA, so let's just store it. config_.options_.insert(*opt); continue; } const Opts& ia_opts = ia->getOptions(); for (Opts::const_iterator iter_ia_opt = ia_opts.begin(); iter_ia_opt != ia_opts.end(); ++iter_ia_opt) { OptionPtr ia_opt = iter_ia_opt->second; Lease6 lease; lease.type_ = (ia->getType() == D6O_IA_NA ? Lease::TYPE_NA : Lease::TYPE_PD); lease.iaid_ = ia->getIAID(); switch (ia_opt->getType()) { case D6O_IAADDR: { Option6IAAddrPtr iaaddr = boost::dynamic_pointer_cast< Option6IAAddr>(ia_opt); if (iaaddr) { lease = Lease6(Lease::TYPE_NA, iaaddr->getAddress(), duid_, ia->getIAID(), iaaddr->getPreferred(), iaaddr->getValid(), 0, hwaddr); lease.cltt_ = time(NULL); lease.state_ = state; applyLease(lease); } } break; case D6O_IAPREFIX: { Option6IAPrefixPtr iaprefix = boost::dynamic_pointer_cast< Option6IAPrefix>(ia_opt); if (iaprefix) { lease = Lease6(Lease::TYPE_PD, iaprefix->getAddress(), duid_, ia->getIAID(), iaprefix->getPreferred(), iaprefix->getValid(), 0, hwaddr, iaprefix->getLength()); lease.cltt_ = time(NULL); applyLease(lease); } } break; case D6O_STATUS_CODE: { // Check if the server has sent status code. If no status // code, assume the status code to be 0. Option6StatusCodePtr status_code = boost::dynamic_pointer_cast< Option6StatusCode>(ia->getOption(D6O_STATUS_CODE)); config_.status_codes_[ia->getIAID()] = (status_code ? status_code->getStatusCode() : 0); } break; default: ; // no-op } } } // Get the global status code. Option6StatusCodePtr status_code = boost::dynamic_pointer_cast< Option6StatusCode>(reply->getOption(D6O_STATUS_CODE)); // If status code has been sent, we override the default status code: // Success and record that we have received the status code. if (status_code) { config_.received_status_code_ = true; config_.status_code_ = status_code->getStatusCode(); } } void Dhcp6Client::applyLease(const Lease6& lease) { // Go over existing leases and try to match the one that we have. for (size_t i = 0; i < config_.leases_.size(); ++i) { Lease6 existing_lease = config_.leases_[i]; // If IAID is matching and there is an actual address assigned // replace the current lease. The default address is :: if the // server hasn't sent the IA option. In this case, there is no // lease assignment so we keep what we have. if ((existing_lease.iaid_ == lease.iaid_) && (existing_lease.type_ == lease.type_) && (lease.addr_ != asiolink::IOAddress("::")) && (existing_lease.addr_ == lease.addr_)) { config_.leases_[i] = lease; return; } } // It is a new lease. Add it. config_.leases_.push_back(lease); } void Dhcp6Client::appendFQDN() { if (fqdn_) { context_.query_->addOption(fqdn_); } } void Dhcp6Client::appendRequestedIAs(const Pkt6Ptr& query) const { BOOST_FOREACH(const ClientIA& ia, client_ias_) { OptionCollection options = query->getOptions(ia.type_ == Lease::TYPE_NA ? D6O_IA_NA : D6O_IA_PD); std::pair option_pair; Option6IAPtr existing_ia; BOOST_FOREACH(option_pair, options) { Option6IAPtr ia_opt = boost::dynamic_pointer_cast(option_pair.second); // This shouldn't happen. if (!ia_opt) { isc_throw(Unexpected, "Dhcp6Client: IA option has an invalid C++ type;" " this is a programming issue"); } if (ia_opt->getIAID() == ia.iaid_) { existing_ia = ia_opt; } } if (!existing_ia) { existing_ia.reset(new Option6IA(ia.type_ == Lease::TYPE_NA ? D6O_IA_NA : D6O_IA_PD, ia.iaid_)); query->addOption(existing_ia); } bool option_exists = false; if ((ia.type_ == Lease::TYPE_NA) && !ia.prefix_.isV6Zero()) { Option6IAAddrPtr ia_addr(new Option6IAAddr(D6O_IAADDR, ia.prefix_, 0, 0)); BOOST_FOREACH(option_pair, existing_ia->getOptions()) { Option6IAAddrPtr existing_addr = boost::dynamic_pointer_cast< Option6IAAddr>(option_pair.second); if (existing_addr && (existing_addr->getAddress() == ia.prefix_)) { option_exists = true; } } if (!option_exists) { existing_ia->addOption(ia_addr); } } else if ((ia.type_ == Lease::TYPE_PD) && (!ia.prefix_.isV6Zero() || (ia.prefix_len_ > 0))) { Option6IAPrefixPtr ia_prefix(new Option6IAPrefix(D6O_IAPREFIX, ia.prefix_, ia.prefix_len_, 0, 0)); BOOST_FOREACH(option_pair, existing_ia->getOptions()) { Option6IAPrefixPtr existing_prefix = boost::dynamic_pointer_cast(option_pair.second); if (existing_prefix && (existing_prefix->getAddress() == ia.prefix_) && existing_prefix->getLength()) { option_exists = true; } } if (!option_exists) { existing_ia->addOption(ia_prefix); } } } } void Dhcp6Client::copyIAs(const Pkt6Ptr& source, const Pkt6Ptr& dest) { typedef OptionCollection Opts; // Copy IA_NAs. Opts opts = source->getOptions(D6O_IA_NA); for (Opts::const_iterator opt = opts.begin(); opt != opts.end(); ++opt) { // Only copy the entire IA_NA if there is at lease one IA Address option. if (opt->second->getOption(D6O_IAADDR)) { dest->addOption(opt->second); } } // Copy IA_PDs. opts = source->getOptions(D6O_IA_PD); for (Opts::const_iterator opt = opts.begin(); opt != opts.end(); ++opt) { // Only copy the entire IA_PD if there is at least one IA Prefix option // in it. if (opt->second->getOption(D6O_IAPREFIX)) { dest->addOption(opt->second); } } } void Dhcp6Client::copyIAsFromLeases(const Pkt6Ptr& dest) const { // Go over leases and create IA_NA and IA_PD options from them. // Create one IA per lease. std::set iaids = getIAIDs(); for (std::set::const_iterator iaid = iaids.begin(); iaid != iaids.end(); ++iaid) { std::vector leases = getLeasesByIAID(*iaid); // Only a valid lease should be included. Do not copy a // lease which have been marked by the server as invalid. if (leases[0].valid_lft_ == 0) { continue; } Option6IAPtr opt(new Option6IA(leases[0].type_ == Lease::TYPE_NA ? D6O_IA_NA : D6O_IA_PD, *iaid)); for (std::vector::const_iterator lease = leases.begin(); lease != leases.end(); ++lease) { if ((lease->preferred_lft_ != 0) && (lease->valid_lft_ != 0)) { if (lease->type_ == Lease::TYPE_NA) { opt->addOption(Option6IAAddrPtr(new Option6IAAddr( D6O_IAADDR, lease->addr_, lease->preferred_lft_, lease->valid_lft_))); } else if (lease->type_ == Lease::TYPE_PD) { opt->addOption(Option6IAAddrPtr(new Option6IAPrefix( D6O_IAPREFIX, lease->addr_, lease->prefixlen_, lease->preferred_lft_, lease->valid_lft_))); } } } dest->addOption(opt); } } void Dhcp6Client::createLease(const Lease6& lease) { applyLease(lease); } Pkt6Ptr Dhcp6Client::createMsg(const uint8_t msg_type) { Pkt6Ptr msg(new Pkt6(msg_type, curr_transid_++)); if (use_client_id_) { msg->addOption(getClientId()); } if (use_oro_) { OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6, D6O_ORO)); oro->setValues(oro_); msg->addOption(oro); }; if (use_docsis_oro_) { OptionUint16ArrayPtr vendor_oro(new OptionUint16Array(Option::V6, DOCSIS3_V6_ORO)); vendor_oro->setValues(docsis_oro_); OptionPtr vendor(new OptionVendor(Option::V6, 4491)); vendor->addOption(vendor_oro); msg->addOption(vendor); } // If there are any custom options specified, add them all to the message. if (!extra_options_.empty()) { for (OptionCollection::iterator opt = extra_options_.begin(); opt != extra_options_.end(); ++opt) { msg->addOption(opt->second); } } // Add classes. for (ClientClasses::const_iterator cclass = classes_.cbegin(); cclass != classes_.cend(); ++cclass) { msg->addClass(*cclass); } return (msg); } void Dhcp6Client::doSARR() { doSolicit(); // Don't send the Request if there was no Advertise. if (context_.response_) { doRequest(); } } void Dhcp6Client::doSolicit(const bool always_apply_config) { context_.query_ = createMsg(DHCPV6_SOLICIT); if (forced_server_id_) { context_.query_->addOption(forced_server_id_); } // Append requested (empty) IAs. appendRequestedIAs(context_.query_); if (use_rapid_commit_) { context_.query_->addOption(OptionPtr(new Option(Option::V6, D6O_RAPID_COMMIT))); } // Add Client FQDN if configured. appendFQDN(); sendMsg(context_.query_); context_.response_ = receiveOneMsg(); // If using Rapid Commit and the server has responded with Reply, // let's apply received configuration. We also apply the configuration // for the Advertise if instructed to do so. if (context_.response_ && (always_apply_config || (use_rapid_commit_ && context_.response_->getType() == DHCPV6_REPLY))) { config_.clear(); applyRcvdConfiguration(context_.response_); } } void Dhcp6Client::doRequest() { Pkt6Ptr query = createMsg(DHCPV6_REQUEST); if (!forced_server_id_) { query->addOption(context_.response_->getOption(D6O_SERVERID)); } else { query->addOption(forced_server_id_); } copyIAs(context_.response_, query); appendRequestedIAs(query); context_.query_ = query; // Add Client FQDN if configured. appendFQDN(); sendMsg(context_.query_); context_.response_ = receiveOneMsg(); /// @todo sanity check here. // Apply new configuration only if the server has responded. if (context_.response_) { config_.clear(); applyRcvdConfiguration(context_.response_); } } void Dhcp6Client::doInfRequest() { context_.query_ = createMsg(DHCPV6_INFORMATION_REQUEST); // IA_NA, IA_TA and IA_PD options are not allowed in INF-REQUEST, // but hey! Let's test it. appendRequestedIAs(context_.query_); sendMsg(context_.query_); context_.response_ = receiveOneMsg(); // Apply new configuration only if the server has responded. if (context_.response_) { config_.clear(); applyRcvdConfiguration(context_.response_); } } void Dhcp6Client::doRenew() { Pkt6Ptr query = createMsg(DHCPV6_RENEW); query->addOption(context_.response_->getOption(D6O_SERVERID)); copyIAsFromLeases(query); // During the Renew the client may request additional bindings per // RFC 8415. appendRequestedIAs(query); context_.query_ = query; // Add Client FQDN if configured. appendFQDN(); sendMsg(context_.query_); context_.response_ = receiveOneMsg(); // Apply configuration only if the server has responded. if (context_.response_) { config_.clear(); applyRcvdConfiguration(context_.response_); } } void Dhcp6Client::doRebind() { Pkt6Ptr query = createMsg(DHCPV6_REBIND); copyIAsFromLeases(query); // During the Rebind the client may request additional bindings per // RFC 8415. appendRequestedIAs(query); // Add Client FQDN if configured. appendFQDN(); context_.query_ = query; sendMsg(context_.query_); context_.response_ = receiveOneMsg(); // Apply configuration only if the server has responded. if (context_.response_) { config_.clear(); applyRcvdConfiguration(context_.response_); } } void Dhcp6Client::doConfirm() { context_.query_ = createMsg(DHCPV6_CONFIRM); copyIAsFromLeases(context_.query_); sendMsg(context_.query_); context_.response_ = receiveOneMsg(); // Set the global status code to default: success and not received. config_.resetGlobalStatusCode(); if (context_.response_) { config_.options_.clear(); applyRcvdConfiguration(context_.response_); } } void Dhcp6Client::doDecline(const bool include_address) { Pkt6Ptr query = createMsg(DHCPV6_DECLINE); if (!forced_server_id_) { query->addOption(context_.response_->getOption(D6O_SERVERID)); } else { query->addOption(forced_server_id_); } generateIAFromLeases(query, include_address); context_.query_ = query; sendMsg(context_.query_); context_.response_ = receiveOneMsg(); // Apply new configuration only if the server has responded. if (context_.response_) { config_.clear(); applyRcvdConfiguration(context_.response_); } } void Dhcp6Client::doRelease() { Pkt6Ptr query = createMsg(DHCPV6_RELEASE); query->addOption(context_.response_->getOption(D6O_SERVERID)); copyIAsFromLeases(query); context_.query_ = query; sendMsg(context_.query_); context_.response_ = receiveOneMsg(); // Apply configuration only if the server has responded. if (context_.response_) { config_.clear(); applyRcvdConfiguration(context_.response_); } } void Dhcp6Client::generateIAFromLeases(const Pkt6Ptr& query, const bool include_address) { /// @todo: add support for IAPREFIX here. for (std::vector::const_iterator lease = config_.leases_.begin(); lease != config_.leases_.end(); ++lease) { if (lease->type_ != Lease::TYPE_NA) { continue; } Option6IAPtr ia(new Option6IA(D6O_IA_NA, lease->iaid_)); if (include_address) { ia->addOption(OptionPtr(new Option6IAAddr(D6O_IAADDR, lease->addr_, lease->preferred_lft_, lease->valid_lft_))); } query->addOption(ia); } } void Dhcp6Client::fastFwdTime(const uint32_t secs, const bool update_server) { // Iterate over all leases and move their cltt backwards. for (size_t i = 0; i < config_.leases_.size(); ++i) { config_.leases_[i].cltt_ -= secs; if (update_server) { Lease6Ptr lease(new Lease6(config_.leases_[i])); LeaseMgrFactory::instance().updateLease6(lease); } } } DuidPtr Dhcp6Client::generateDUID(DUID::DUIDType duid_type) const { std::vector duid; /// @todo remove this check once other DUID types are implemented. if (duid_type != DUID::DUID_LLT) { isc_throw(NotImplemented, "currently the Dhcp6Client only supports" " generation of DUID LLT"); } duid.push_back(static_cast(duid_type)); for (int i = 0; i < 4; ++i) { duid.push_back(static_cast(rand() % 255)); } for (int i = 0; i < 6; ++i) { duid.push_back(static_cast(i)); } return (DuidPtr(new DUID(duid))); } OptionPtr Dhcp6Client::getClientId() const { OptionPtr opt_client_id(new Option(Option::V6, D6O_CLIENTID, duid_->getDuid().begin(), duid_->getDuid().end())); return (opt_client_id); } std::set Dhcp6Client::getIAIDs() const { std::set iaids; for (std::vector::const_iterator lease = config_.leases_.begin(); lease != config_.leases_.end(); ++lease) { iaids.insert(lease->iaid_); } return (iaids); } std::vector Dhcp6Client::getLeasesByIAID(const uint32_t iaid) const { std::vector leases; getLeasesByProperty(iaid, true, leases); return (leases); } template void Dhcp6Client::getLeasesByProperty(const PropertyType& property, const bool equals, std::vector& leases) const { getLeasesByPropertyFun fun; fun(config_, property, equals, leases); } std::vector Dhcp6Client::getLeasesByType(const Lease6::Type& lease_type) const { std::vector leases; getLeasesByProperty(lease_type, true, leases); return (leases); } std::vector Dhcp6Client::getLeasesWithNonZeroLifetime() const { std::vector leases; getLeasesByProperty(0, false, leases); return (leases); } std::vector Dhcp6Client::getLeasesWithZeroLifetime() const { std::vector leases; getLeasesByProperty(0, true, leases); return (leases); } std::vector Dhcp6Client::getLeasesByAddress(const IOAddress& address) const { std::vector leases; getLeasesByProperty(address, true, leases); return (leases); } std::vector Dhcp6Client::getLeasesByAddressRange(const IOAddress& first, const IOAddress& second) const { std::vector leases; getLeasesByPool(config_, Pool6(Lease::TYPE_NA, first, second), leases); return (leases); } std::vector Dhcp6Client::getLeasesByPrefixPool(const asiolink::IOAddress& prefix, const uint8_t prefix_len, const uint8_t delegated_len) const { std::vector leases; getLeasesByPool(config_, Pool6(Lease::TYPE_PD, prefix, prefix_len, delegated_len), leases); return (leases); } bool Dhcp6Client::hasLeaseForAddress(const asiolink::IOAddress& address) const { std::vector leases = getLeasesByAddress(address); return (!leases.empty()); } bool Dhcp6Client::hasLeaseForAddress(const asiolink::IOAddress& address, const IAID& iaid) const { std::vector leases = getLeasesByAddress(address); BOOST_FOREACH(const Lease6& lease, leases) { if (lease.iaid_ == iaid) { return (true); } } return (false); } bool Dhcp6Client::hasLeaseForAddressRange(const asiolink::IOAddress& first, const asiolink::IOAddress& last) const { std::vector leases = getLeasesByAddressRange(first, last); return (!leases.empty()); } bool Dhcp6Client::hasLeaseWithZeroLifetimeForAddress(const asiolink::IOAddress& address) const { std::vector leases = getLeasesByAddress(address); BOOST_FOREACH(const Lease6& lease, leases) { if ((lease.preferred_lft_ == 0) && (lease.valid_lft_ == 0)) { return (true); } } return (false); } bool Dhcp6Client::hasLeaseForPrefix(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const { std::vector leases = getLeasesByAddress(prefix); BOOST_FOREACH(const Lease6& lease, leases) { if (lease.prefixlen_ == prefix_len) { return (true); } } return (false); } bool Dhcp6Client::hasLeaseForPrefix(const asiolink::IOAddress& prefix, const uint8_t prefix_len, const IAID& iaid) const { std::vector leases = getLeasesByAddress(prefix); BOOST_FOREACH(const Lease6& lease, leases) { if ((lease.prefixlen_ == prefix_len) && (lease.iaid_ == iaid)) { return (true); } } return (false); } bool Dhcp6Client::hasLeaseForPrefixPool(const asiolink::IOAddress& prefix, const uint8_t prefix_len, const uint8_t delegated_len) const { std::vector leases = getLeasesByPrefixPool(prefix, prefix_len, delegated_len); return (!leases.empty()); } bool Dhcp6Client::hasLeaseWithZeroLifetimeForPrefix(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const { std::vector leases = getLeasesByAddress(prefix); BOOST_FOREACH(const Lease6& lease, leases) { if ((lease.prefixlen_ == prefix_len) && (lease.preferred_lft_ == 0) && (lease.valid_lft_ == 0)) { return (true); } } return (false); } bool Dhcp6Client::hasOptionWithAddress(const uint16_t option_type, const std::string& expected_address) const { Option6AddrLstPtr opt = boost::dynamic_pointer_cast< Option6AddrLst>(config_.findOption(option_type)); if (opt) { Option6AddrLst::AddressContainer addrs = opt->getAddresses(); if (!addrs.empty()) { return (std::find(addrs.begin(), addrs.end(), IOAddress(expected_address)) != addrs.end()); } } return (false); } uint16_t Dhcp6Client::getStatusCode(const uint32_t iaid) const { std::map::const_iterator status_code = config_.status_codes_.find(iaid); if (status_code == config_.status_codes_.end()) { if (!getLeasesByIAID(iaid).empty()) { return (STATUS_Success); } } else { return (status_code->second); } return (0xFFFF); } bool Dhcp6Client::getTeeTimes(const uint32_t iaid, uint32_t& t1, uint32_t& t2) const { // Sanity check. if (!context_.response_) { return (false); } // Get all options in the response message and pick IA_NA, IA_PD. OptionCollection opts = context_.response_->options_; for (auto opt = opts.begin(); opt != opts.end(); ++opt) { Option6IAPtr ia = boost::dynamic_pointer_cast(opt->second); if (!ia) { // This is not IA, so let's just skip it. continue; } if (ia->getIAID() != iaid) { // This is not the right IA, so let's just skip it. continue; } // Found the IA. t1 = ia->getT1(); t2 = ia->getT2(); return (true); } // Not found the IA. return (false); } void Dhcp6Client::setDUID(const std::string& str) { DUID d = DUID::fromText(str); duid_.reset(new DUID(d)); } void Dhcp6Client::modifyDUID() { if (!duid_) { duid_ = generateDUID(DUID::DUID_LLT); return; } std::vector new_duid = duid_->getDuid(); // Modify the DUID by adding 1 to its last byte. ++new_duid[new_duid.size() - 1]; duid_.reset(new DUID(new_duid)); } Pkt6Ptr Dhcp6Client::receiveOneMsg() { return (srv_->receiveOneMsg()); } void Dhcp6Client::receiveResponse() { context_.response_ = receiveOneMsg(); // If the server has responded, store the configuration received. if (context_.response_) { config_.clear(); applyRcvdConfiguration(context_.response_); } } void Dhcp6Client::sendMsg(const Pkt6Ptr& msg) { srv_->shutdown_ = false; // The client is configured to send through the relay. We achieve that // adding the relay structure. if (use_relay_ || !relay_info_.empty()) { if (relay_info_.empty()) { // Let's craft the relay info by hand Pkt6::RelayInfo relay; relay.linkaddr_ = relay_link_addr_; relay.peeraddr_ = asiolink::IOAddress("fe80::1"); relay.msg_type_ = DHCPV6_RELAY_FORW; relay.hop_count_ = 1; // Interface identifier, if specified. if (interface_id_) { relay.options_.insert(std::make_pair(D6O_INTERFACE_ID, interface_id_)); } msg->relay_info_.push_back(relay); } else { // The test provided relay_info_, let's use that. msg->relay_info_ = relay_info_; } } // Repack the message to simulate wire-data parsing. msg->pack(); Pkt6Ptr msg_copy(new Pkt6(static_cast (msg->getBuffer().getData()), msg->getBuffer().getLength())); msg_copy->setRemoteAddr(link_local_); msg_copy->setLocalAddr(dest_addr_); msg_copy->setIface(iface_name_); msg_copy->setIndex(iface_index_); // Copy classes const ClientClasses& classes = msg->getClasses(); for (ClientClasses::const_iterator cclass = classes.cbegin(); cclass != classes.cend(); ++cclass) { msg_copy->addClass(*cclass); } srv_->fakeReceive(msg_copy); try { // Invoke run_one instead of run, because we want to avoid triggering // IO service. srv_->run_one(); } catch (...) { // Suppress errors, as the DHCPv6 server does. } // Make sure the server processed all packets in MT. isc::util::MultiThreadingMgr::instance().getThreadPool().wait(3); } void Dhcp6Client::requestAddress(const uint32_t iaid, const asiolink::IOAddress& address) { client_ias_.push_back(ClientIA(Lease::TYPE_NA, iaid, address, 128)); } void Dhcp6Client::requestPrefix(const uint32_t iaid, const uint8_t prefix_len, const asiolink::IOAddress& prefix) { client_ias_.push_back(ClientIA(Lease::TYPE_PD, iaid, prefix, prefix_len)); } void Dhcp6Client::useInterfaceId(const std::string& interface_id) { OptionBuffer buf(interface_id.begin(), interface_id.end()); interface_id_.reset(new Option(Option::V6, D6O_INTERFACE_ID, buf)); } void Dhcp6Client::useFQDN(const uint8_t flags, const std::string& fqdn_name, Option6ClientFqdn::DomainNameType fqdn_type) { fqdn_.reset(new Option6ClientFqdn(flags, fqdn_name, fqdn_type)); } void Dhcp6Client::addExtraOption(const OptionPtr& opt) { extra_options_.insert(std::make_pair(opt->getType(), opt)); } void Dhcp6Client::clearExtraOptions() { extra_options_.clear(); } void Dhcp6Client::addClass(const ClientClass& client_class) { if (!classes_.contains(client_class)) { classes_.insert(client_class); } } void Dhcp6Client::clearClasses() { classes_.clear(); } void Dhcp6Client::printConfiguration() const { // Print DUID std::cout << "Client " << (duid_ ? duid_->toText() : "(without duid)") << " got " << getLeaseNum() << " lease(s): "; // Print leases for (int i = 0; i < getLeaseNum(); i++) { Lease6 lease = getLease(i); std::cout << lease.addr_.toText() << " "; } /// @todo: Print many other parameters. std::cout << std::endl; } } // end of namespace isc::dhcp::test } // end of namespace isc::dhcp } // end of namespace isc