mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 05:55:28 +00:00
[master] Merge branch 'trac2949' (inf-request in dhcpv6)
This commit is contained in:
@@ -10,6 +10,7 @@ nobase_dist_doc_DATA += examples/kea6/simple.json
|
||||
nobase_dist_doc_DATA += examples/kea6/several-subnets.json
|
||||
nobase_dist_doc_DATA += examples/kea6/multiple-options.json
|
||||
nobase_dist_doc_DATA += examples/kea6/advanced.json
|
||||
nobase_dist_doc_DATA += examples/kea6/stateless.json
|
||||
nobase_dist_doc_DATA += examples/ddns/sample1.json
|
||||
nobase_dist_doc_DATA += examples/ddns/template.json
|
||||
|
||||
|
23
doc/examples/kea6/stateless.json
Normal file
23
doc/examples/kea6/stateless.json
Normal file
@@ -0,0 +1,23 @@
|
||||
# A very simply stateless configuration that provides information about DNS
|
||||
# servers to all clients, regardless of their point of attachment.
|
||||
#
|
||||
# It is also possible to specify options on a per subnet basis
|
||||
# in the same way as in stateful mode.
|
||||
#
|
||||
|
||||
{
|
||||
"Dhcp6": {
|
||||
"interfaces": [ "ethX" ],
|
||||
|
||||
# This is the list of options that will be granted to all clients that ask.
|
||||
"option-data": [ {
|
||||
"name": "dns-servers",
|
||||
"data": "2001:db8::1, 2001:db8::2"
|
||||
} ],
|
||||
|
||||
# Kea 0.9.1 requires lease-database to be specified, even it is not used.
|
||||
# In stateless mode, only options are granted, not addresses or prefixes, so
|
||||
# there will be no leases (unless stateless and stateful mode is used together).
|
||||
"lease-database": { "type": "memfile" }
|
||||
}
|
||||
}
|
@@ -1743,6 +1743,49 @@ should include options from the isc option space:
|
||||
|
||||
</section>
|
||||
|
||||
<section id="stateless-dhcp6">
|
||||
<title>Stateless DHCPv6 (Information-Request Message)</title>
|
||||
<para>Typically DHCPv6 is used to assign both addresses and options. These
|
||||
assignments (leases) have state that changes over time, hence
|
||||
their name, stateful. DHCPv6 also supports a stateless mode,
|
||||
where clients request configuration options only. This mode is
|
||||
considered lightweight from the server perspective, as it does not require
|
||||
any state tracking; hence its name.</para>
|
||||
<para>The Kea server supports stateless mode. Clients can send
|
||||
Information-Request messages and the server will send back
|
||||
answers with the requested options (providing the options are
|
||||
available in the server configuration). The server will attempt to
|
||||
use per-subnet options first. If that fails - for whatever reason - it
|
||||
will then try to provide options defined in the global scope.</para>
|
||||
|
||||
<para>Stateless and stateful mode can be used together. No special
|
||||
configuration directives are required to handle this. Simply use the
|
||||
configuration for stateful clients and the stateless clients will get
|
||||
just options they requested.</para>
|
||||
|
||||
<para>This usage of global options allows for an interesting case.
|
||||
It is possible to run a server that provides just options and no
|
||||
addresses or prefixes. If the options have the same value in each
|
||||
subnet, the configuration can define required options in the global
|
||||
scope and skip subnet definitions altogether. Here's a simple example of
|
||||
such a configuration:
|
||||
<screen>
|
||||
"Dhcp6": {
|
||||
"interfaces": [ "ethX" ],
|
||||
<userinput>"option-data": [ {
|
||||
"name": "dns-servers",
|
||||
"data": "2001:db8::1, 2001:db8::2"
|
||||
} ]</userinput>,
|
||||
"lease-database": { "type": "memfile" }
|
||||
}
|
||||
</screen>
|
||||
This very simple configuration will provide DNS server information
|
||||
to all clients in the network, regardless of their location. Note the
|
||||
specification of the memfile lease database: this is required since,
|
||||
as of version 0.9.1, Kea requires a lease database to be specified
|
||||
even if it is not used.</para>
|
||||
</section>
|
||||
|
||||
<section id="dhcp6-relay-override">
|
||||
<title>Using specific relay agent for a subnet</title>
|
||||
<para>
|
||||
@@ -2051,8 +2094,7 @@ should include options from the isc option space:
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
Duplication report (DECLINE), stateless configuration
|
||||
(INFORMATION-REQUEST) and client reconfiguration (RECONFIGURE) are
|
||||
Duplication report (DECLINE) and client reconfiguration (RECONFIGURE) are
|
||||
not yet supported.
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
@@ -701,7 +701,7 @@ Dhcpv6Srv::generateServerID() {
|
||||
}
|
||||
|
||||
void
|
||||
Dhcpv6Srv::copyDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
|
||||
Dhcpv6Srv::copyClientOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
|
||||
// Add client-id.
|
||||
OptionPtr clientid = question->getOption(D6O_CLIENTID);
|
||||
if (clientid) {
|
||||
@@ -724,29 +724,43 @@ Dhcpv6Srv::appendDefaultOptions(const Pkt6Ptr&, Pkt6Ptr& answer) {
|
||||
|
||||
void
|
||||
Dhcpv6Srv::appendRequestedOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
|
||||
// Get the configured subnet suitable for the incoming packet.
|
||||
Subnet6Ptr subnet = selectSubnet(question);
|
||||
// Leave if there is no subnet matching the incoming packet.
|
||||
// There is no need to log the error message here because
|
||||
// it will be logged in the assignLease() when it fails to
|
||||
// pick the suitable subnet. We don't want to duplicate
|
||||
// error messages in such case.
|
||||
if (!subnet) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Client requests some options using ORO option. Try to
|
||||
// get this option from client's message.
|
||||
boost::shared_ptr<OptionIntArray<uint16_t> > option_oro =
|
||||
boost::dynamic_pointer_cast<OptionIntArray<uint16_t> >(question->getOption(D6O_ORO));
|
||||
// Option ORO not found. Don't do anything then.
|
||||
boost::dynamic_pointer_cast<OptionIntArray<uint16_t> >
|
||||
(question->getOption(D6O_ORO));
|
||||
|
||||
// Option ORO not found? We're done here then.
|
||||
if (!option_oro) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get global option definitions (i.e. options defined to apply to all,
|
||||
// unless overwritten on a subnet or host level)
|
||||
ConstCfgOptionPtr global_opts = CfgMgr::instance().getCurrentCfg()->
|
||||
getCfgOption();
|
||||
|
||||
// Get the configured subnet suitable for the incoming packet.
|
||||
// It may be NULL (if server is misconfigured or the client was rejected
|
||||
// using client classes).
|
||||
Subnet6Ptr subnet = selectSubnet(question);
|
||||
|
||||
// Get the list of options that client requested.
|
||||
const std::vector<uint16_t>& requested_opts = option_oro->getValues();
|
||||
BOOST_FOREACH(uint16_t opt, requested_opts) {
|
||||
OptionDescriptor desc = subnet->getCfgOption()->get("dhcp6", opt);
|
||||
// If we found a subnet for this client, try subnet first.
|
||||
if (subnet) {
|
||||
OptionDescriptor desc = subnet->getCfgOption()->get("dhcp6", opt);
|
||||
if (desc.option_) {
|
||||
// Attempt to assign an option from subnet first.
|
||||
answer->addOption(desc.option_);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If subnet specific option is not there, try global.
|
||||
OptionDescriptor desc = global_opts->get("dhcp6", opt);
|
||||
if (desc.option_) {
|
||||
answer->addOption(desc.option_);
|
||||
}
|
||||
@@ -2272,7 +2286,7 @@ Dhcpv6Srv::processSolicit(const Pkt6Ptr& solicit) {
|
||||
|
||||
Pkt6Ptr advertise(new Pkt6(DHCPV6_ADVERTISE, solicit->getTransid()));
|
||||
|
||||
copyDefaultOptions(solicit, advertise);
|
||||
copyClientOptions(solicit, advertise);
|
||||
appendDefaultOptions(solicit, advertise);
|
||||
appendRequestedOptions(solicit, advertise);
|
||||
appendRequestedVendorOptions(solicit, advertise);
|
||||
@@ -2295,7 +2309,7 @@ Dhcpv6Srv::processRequest(const Pkt6Ptr& request) {
|
||||
|
||||
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, request->getTransid()));
|
||||
|
||||
copyDefaultOptions(request, reply);
|
||||
copyClientOptions(request, reply);
|
||||
appendDefaultOptions(request, reply);
|
||||
appendRequestedOptions(request, reply);
|
||||
appendRequestedVendorOptions(request, reply);
|
||||
@@ -2315,7 +2329,7 @@ Dhcpv6Srv::processRenew(const Pkt6Ptr& renew) {
|
||||
|
||||
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, renew->getTransid()));
|
||||
|
||||
copyDefaultOptions(renew, reply);
|
||||
copyClientOptions(renew, reply);
|
||||
appendDefaultOptions(renew, reply);
|
||||
appendRequestedOptions(renew, reply);
|
||||
|
||||
@@ -2332,7 +2346,7 @@ Dhcpv6Srv::processRebind(const Pkt6Ptr& rebind) {
|
||||
|
||||
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, rebind->getTransid()));
|
||||
|
||||
copyDefaultOptions(rebind, reply);
|
||||
copyClientOptions(rebind, reply);
|
||||
appendDefaultOptions(rebind, reply);
|
||||
appendRequestedOptions(rebind, reply);
|
||||
|
||||
@@ -2356,7 +2370,7 @@ Dhcpv6Srv::processConfirm(const Pkt6Ptr& confirm) {
|
||||
// The server sends Reply message in response to Confirm.
|
||||
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, confirm->getTransid()));
|
||||
// Make sure that the necessary options are included.
|
||||
copyDefaultOptions(confirm, reply);
|
||||
copyClientOptions(confirm, reply);
|
||||
appendDefaultOptions(confirm, reply);
|
||||
// Indicates if at least one address has been verified. If no addresses
|
||||
// are verified it means that the client has sent no IA_NA options
|
||||
@@ -2430,7 +2444,7 @@ Dhcpv6Srv::processRelease(const Pkt6Ptr& release) {
|
||||
|
||||
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, release->getTransid()));
|
||||
|
||||
copyDefaultOptions(release, reply);
|
||||
copyClientOptions(release, reply);
|
||||
appendDefaultOptions(release, reply);
|
||||
|
||||
releaseLeases(release, reply);
|
||||
@@ -2445,14 +2459,27 @@ Pkt6Ptr
|
||||
Dhcpv6Srv::processDecline(const Pkt6Ptr& decline) {
|
||||
/// @todo: Implement this
|
||||
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, decline->getTransid()));
|
||||
return reply;
|
||||
return (reply);
|
||||
}
|
||||
|
||||
Pkt6Ptr
|
||||
Dhcpv6Srv::processInfRequest(const Pkt6Ptr& infRequest) {
|
||||
/// @todo: Implement this
|
||||
|
||||
// Create a Reply packet, with the same trans-id as the client's.
|
||||
Pkt6Ptr reply(new Pkt6(DHCPV6_REPLY, infRequest->getTransid()));
|
||||
return reply;
|
||||
|
||||
// Copy client options (client-id, also relay information if present)
|
||||
copyClientOptions(infRequest, reply);
|
||||
|
||||
// Append default options, i.e. options that the server is supposed
|
||||
// to put in all messages it sends (server-id for now, but possibly other
|
||||
// options once we start supporting authentication)
|
||||
appendDefaultOptions(infRequest, reply);
|
||||
|
||||
// Try to assign options that were requested by the client.
|
||||
appendRequestedOptions(infRequest, reply);
|
||||
|
||||
return (reply);
|
||||
}
|
||||
|
||||
size_t
|
||||
|
@@ -394,10 +394,11 @@ protected:
|
||||
/// Copies options that must appear in any server response (ADVERTISE, REPLY)
|
||||
/// to client's messages (SOLICIT, REQUEST, RENEW, REBIND, DECLINE, RELEASE).
|
||||
/// One notable example is client-id. Other options may be copied as required.
|
||||
/// Relay information details are also copied here.
|
||||
///
|
||||
/// @param question client's message (options will be copied from here)
|
||||
/// @param answer server's message (options will be copied here)
|
||||
void copyDefaultOptions(const Pkt6Ptr& question, Pkt6Ptr& answer);
|
||||
void copyClientOptions(const Pkt6Ptr& question, Pkt6Ptr& answer);
|
||||
|
||||
/// @brief Appends default options to server's answer.
|
||||
///
|
||||
|
@@ -25,7 +25,7 @@ AM_CPPFLAGS += -DTOP_BUILDDIR="\"$(top_builddir)\""
|
||||
AM_CPPFLAGS += $(BOOST_INCLUDES)
|
||||
AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
|
||||
|
||||
CLEANFILES = $(builddir)/interfaces.txt $(builddir)/logger_lockfile
|
||||
CLEANFILES = $(builddir)/logger_lockfile
|
||||
CLEANFILES += $(builddir)/load_marker.txt $(builddir)/unload_marker.txt
|
||||
CLEANFILES += *.json *.log
|
||||
|
||||
@@ -84,6 +84,7 @@ dhcp6_unittests_SOURCES += rebind_unittest.cc
|
||||
dhcp6_unittests_SOURCES += sarr_unittest.cc
|
||||
dhcp6_unittests_SOURCES += config_parser_unittest.cc
|
||||
dhcp6_unittests_SOURCES += confirm_unittest.cc
|
||||
dhcp6_unittests_SOURCES += infrequest_unittest.cc
|
||||
dhcp6_unittests_SOURCES += dhcp6_message_test.cc dhcp6_message_test.h
|
||||
|
||||
if CONFIG_BACKEND_BUNDY
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <dhcp/option_custom.h>
|
||||
#include <dhcp/option6_ia.h>
|
||||
#include <dhcp/option6_iaaddr.h>
|
||||
#include <dhcp/option_int_array.h>
|
||||
#include <dhcp/pkt6.h>
|
||||
#include <dhcpsrv/lease.h>
|
||||
#include <dhcp6/tests/dhcp6_client.h>
|
||||
@@ -40,6 +41,8 @@ Dhcp6Client::Dhcp6Client() :
|
||||
use_na_(false),
|
||||
use_pd_(false),
|
||||
use_relay_(false),
|
||||
use_oro_(false),
|
||||
use_client_id_(true),
|
||||
prefix_hint_() {
|
||||
}
|
||||
|
||||
@@ -52,7 +55,10 @@ Dhcp6Client::Dhcp6Client(boost::shared_ptr<NakedDhcpv6Srv>& srv) :
|
||||
srv_(srv),
|
||||
use_na_(false),
|
||||
use_pd_(false),
|
||||
use_relay_(false) {
|
||||
use_relay_(false),
|
||||
use_oro_(false),
|
||||
use_client_id_(true),
|
||||
prefix_hint_() {
|
||||
}
|
||||
|
||||
void
|
||||
@@ -245,7 +251,17 @@ Dhcp6Client::createLease(const Lease6& lease) {
|
||||
Pkt6Ptr
|
||||
Dhcp6Client::createMsg(const uint8_t msg_type) {
|
||||
Pkt6Ptr msg(new Pkt6(msg_type, curr_transid_++));
|
||||
msg->addOption(getClientId());
|
||||
|
||||
if (use_client_id_) {
|
||||
msg->addOption(getClientId());
|
||||
}
|
||||
if (use_oro_) {
|
||||
OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6, D6O_ORO));
|
||||
oro->setValues(oro_);
|
||||
|
||||
msg->addOption(oro);
|
||||
};
|
||||
|
||||
return (msg);
|
||||
}
|
||||
|
||||
@@ -295,6 +311,32 @@ Dhcp6Client::doRequest() {
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
if (use_na_) {
|
||||
// Insert IA_NA option with iaid=1234.
|
||||
context_.query_->addOption(Option6IAPtr(new Option6IA(D6O_IA_NA,
|
||||
1234)));
|
||||
}
|
||||
|
||||
// IA-PD is also not allowed. So it may be useful in testing, too.
|
||||
if (use_pd_) {
|
||||
// Insert IA_PD option with iaid=5678
|
||||
Option6IAPtr ia(new Option6IA(D6O_IA_PD, 5678));
|
||||
if (prefix_hint_) {
|
||||
ia->addOption(prefix_hint_);
|
||||
}
|
||||
context_.query_->addOption(ia);
|
||||
}
|
||||
|
||||
sendMsg(context_.query_);
|
||||
context_.response_ = receiveOneMsg();
|
||||
}
|
||||
|
||||
void
|
||||
Dhcp6Client::doRebind() {
|
||||
Pkt6Ptr query = createMsg(DHCPV6_REBIND);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
@@ -217,6 +218,16 @@ public:
|
||||
/// receiving server's response (if any).
|
||||
void doConfirm();
|
||||
|
||||
|
||||
/// @brief Performs stateless (inf-request / reply) exchange.
|
||||
///
|
||||
/// This function generates Information-request message, sends it
|
||||
/// to the server and then receives the reply. Contents of the Inf-Request
|
||||
/// are controlled by use_na_, use_pd_, use_client_id_ and use_oro_
|
||||
/// fields. This method does not process the response in any specific
|
||||
/// way, just stores it.
|
||||
void doInfRequest();
|
||||
|
||||
/// @brief Removes the stateful configuration obtained from the server.
|
||||
///
|
||||
/// It removes all leases held by the client.
|
||||
@@ -365,12 +376,36 @@ public:
|
||||
relay_link_addr_ = link_addr;
|
||||
}
|
||||
|
||||
/// @brief Controls whether the client should send a client-id or not
|
||||
/// @param send should the client-id be sent?
|
||||
void useClientId(bool send) {
|
||||
use_client_id_ = send;
|
||||
}
|
||||
|
||||
/// @brief Lease configuration obtained by the client.
|
||||
Configuration config_;
|
||||
|
||||
/// @brief Link address of the relay to be used for relayed messages.
|
||||
asiolink::IOAddress relay_link_addr_;
|
||||
|
||||
/// @brief Controls whether the client will send ORO
|
||||
///
|
||||
/// The actual content of the ORO is specified in oro_.
|
||||
/// It is useful to split the actual content and the ORO sending
|
||||
/// decision, so we could test cases of sending empty ORO.
|
||||
/// @param send controls whether ORO will be sent or not.
|
||||
void useORO(bool send) {
|
||||
use_oro_ = send;
|
||||
}
|
||||
|
||||
/// @brief Instructs client to request specified option in ORO
|
||||
///
|
||||
/// @param option_code client will request this option code
|
||||
void requestOption(uint16_t option_code) {
|
||||
use_oro_ = true;
|
||||
oro_.push_back(option_code);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/// @brief Applies the new leases for the client.
|
||||
@@ -470,8 +505,17 @@ private:
|
||||
bool use_pd_; ///< Enable prefix delegation.
|
||||
bool use_relay_; ///< Enable relaying messages to the server.
|
||||
|
||||
bool use_oro_; ///< Conth
|
||||
bool use_client_id_;
|
||||
|
||||
/// @brief Pointer to the option holding a prefix hint.
|
||||
Option6IAPrefixPtr prefix_hint_;
|
||||
|
||||
/// @brief List of options to be requested
|
||||
///
|
||||
/// Content of this vector will be sent as ORO if use_oro_ is set
|
||||
/// to true. See @ref sendORO for details.
|
||||
std::vector<uint16_t> oro_;
|
||||
};
|
||||
|
||||
} // end of namespace isc::dhcp::test
|
||||
|
@@ -186,15 +186,10 @@ TEST_F(NakedDhcpv6SrvTest, ReleaseNoSubnet) {
|
||||
checkNakResponse (reply, DHCPV6_REPLY, 1234, STATUS_NoBinding);
|
||||
}
|
||||
|
||||
|
||||
// Test verifies that the Dhcpv6_srv class can be instantiated. It checks a mode
|
||||
// without open sockets and with sockets opened on a high port (to not require
|
||||
// root privileges).
|
||||
TEST_F(Dhcpv6SrvTest, basic) {
|
||||
// srv has stubbed interface detection. It will read
|
||||
// interfaces.txt instead. It will pretend to have detected
|
||||
// fe80::1234 link-local address on eth0 interface. Obviously
|
||||
// an attempt to bind this socket will fail.
|
||||
boost::scoped_ptr<Dhcpv6Srv> srv;
|
||||
|
||||
ASSERT_NO_THROW( {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <dhcp6/tests/dhcp6_test_utils.h>
|
||||
#include <dhcp6/json_config_parser.h>
|
||||
#include <config/ccsession.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace isc::data;
|
||||
using namespace isc::dhcp;
|
||||
@@ -621,5 +622,35 @@ NakedDhcpv6SrvTest::generateIA(uint16_t type, uint32_t iaid, uint32_t t1,
|
||||
return (ia);
|
||||
}
|
||||
|
||||
bool
|
||||
Dhcpv6SrvTest::compareOptions(const isc::dhcp::OptionPtr& option1,
|
||||
const isc::dhcp::OptionPtr& option2) {
|
||||
if ((!option1 && option2) || (option1 && !option2)) {
|
||||
return (false);
|
||||
}
|
||||
if (!option1 && !option2) {
|
||||
return (true);
|
||||
}
|
||||
|
||||
// We could start by comparing option codes and option lengths
|
||||
// here, but it's just a waste of time. These are tests, so they
|
||||
// don't have to be super performant. The pack+memcmp approach
|
||||
// verifies all in one go.
|
||||
|
||||
isc::util::OutputBuffer buf1(0);
|
||||
isc::util::OutputBuffer buf2(0);
|
||||
|
||||
option1->pack(buf1);
|
||||
option2->pack(buf2);
|
||||
|
||||
if (buf1.getLength() != buf2.getLength()) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
// memcmp returns 0 when equal.
|
||||
return (!memcmp(buf1.getData(), buf2.getData(), buf1.getLength()));
|
||||
}
|
||||
|
||||
|
||||
}; // end of isc::test namespace
|
||||
}; // end of isc namespace
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -462,6 +462,22 @@ public:
|
||||
const isc::asiolink::IOAddress& addr,
|
||||
const uint8_t prefix_len, const uint32_t iaid);
|
||||
|
||||
/// @brief Compare options
|
||||
///
|
||||
/// This method compares whether options content is identical. It writes
|
||||
/// both options to a buffer and then compares the buffers. Comparing
|
||||
/// two different instances of an option that has identical content
|
||||
/// will return true.
|
||||
///
|
||||
/// It is safe to pass NULL pointers. Two NULL pointers are equal.
|
||||
/// NULL pointer and non-NULL pointers are obviously non-equal.
|
||||
///
|
||||
/// @param option1 pointer to the first option
|
||||
/// @param option2
|
||||
/// @return true, if content is identical
|
||||
bool compareOptions(const isc::dhcp::OptionPtr& option1,
|
||||
const isc::dhcp::OptionPtr& option2);
|
||||
|
||||
/// @brief Performs basic (positive) RENEW test
|
||||
///
|
||||
/// See renewBasic and pdRenewBasic tests for detailed explanation.
|
||||
|
299
src/bin/dhcp6/tests/infrequest_unittest.cc
Normal file
299
src/bin/dhcp6/tests/infrequest_unittest.cc
Normal file
@@ -0,0 +1,299 @@
|
||||
// Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
||||
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#include <config.h>
|
||||
#include <dhcp/tests/iface_mgr_test_config.h>
|
||||
#include <dhcp6/tests/dhcp6_test_utils.h>
|
||||
#include <dhcp6/tests/dhcp6_client.h>
|
||||
#include <dhcp/option6_addrlst.h>
|
||||
#include <dhcp/option6_client_fqdn.h>
|
||||
|
||||
using namespace isc;
|
||||
using namespace isc::dhcp;
|
||||
using namespace isc::dhcp::test;
|
||||
using namespace isc::test;
|
||||
|
||||
namespace {
|
||||
|
||||
/// @brief Set of JSON configurations used by the Information-Request unit tests.
|
||||
///
|
||||
/// - Configuration 0:
|
||||
/// - one subnet used on eth0 interface
|
||||
/// - with address and prefix pools
|
||||
/// - dns-servers option
|
||||
/// - Configuation 1:
|
||||
/// - one subnet used on eth0 interface
|
||||
/// - no addresses or prefixes
|
||||
/// - domain-search option
|
||||
/// - Configuration 2:
|
||||
/// - one subnet used on eth0 interface
|
||||
/// - dns-servers option for subnet
|
||||
/// - sip-servers defined in global scope
|
||||
/// - Configuration 3:
|
||||
/// - nis-server, nis-domain specified in global scope
|
||||
/// - no subnets defined
|
||||
const char* CONFIGS[] = {
|
||||
// Configuration 0
|
||||
"{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet6\": [ { "
|
||||
" \"pools\": [ { \"pool\": \"2001:db8:2::/64\" } ],"
|
||||
" \"pd-pools\": ["
|
||||
" { \"prefix\": \"2001:db8:3::\", "
|
||||
" \"prefix-len\": 48, "
|
||||
" \"delegated-len\": 64"
|
||||
" } ],"
|
||||
" \"option-data\": [ {"
|
||||
" \"name\": \"dns-servers\","
|
||||
" \"data\": \"2001:db8::1, 2001:db8::2\""
|
||||
" } ],"
|
||||
" \"subnet\": \"2001:db8::/32\", "
|
||||
" \"interface\": \"eth0\""
|
||||
" } ],"
|
||||
"\"valid-lifetime\": 4000 }",
|
||||
|
||||
// Configuration 1
|
||||
"{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"subnet6\": [ { "
|
||||
" \"option-data\": [ {"
|
||||
" \"name\": \"sip-server-addr\","
|
||||
" \"data\": \"2001:db8::abcd\""
|
||||
" } ],"
|
||||
" \"subnet\": \"2001:db8::/32\", "
|
||||
" \"interface\": \"eth0\""
|
||||
" } ],"
|
||||
"\"valid-lifetime\": 4000 }",
|
||||
|
||||
// Configuration 2
|
||||
"{ \"interfaces\": [ \"*\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
" \"option-data\": [ {"
|
||||
" \"name\": \"sip-server-addr\","
|
||||
" \"data\": \"2001:db8::1\""
|
||||
" } ],"
|
||||
"\"subnet6\": [ { "
|
||||
" \"subnet\": \"2001:db8::/32\", "
|
||||
" \"interface\": \"eth0\","
|
||||
" \"option-data\": [ {"
|
||||
" \"name\": \"dns-servers\","
|
||||
" \"data\": \"2001:db8::2\""
|
||||
" } ]"
|
||||
" } ],"
|
||||
"\"valid-lifetime\": 4000 }",
|
||||
|
||||
// Configuration 3
|
||||
"{ \"interfaces\": [ \"*\" ],"
|
||||
"\"option-data\": [ {"
|
||||
" \"name\": \"nis-servers\","
|
||||
" \"data\": \"2001:db8::1, 2001:db8::2\""
|
||||
" } ]"
|
||||
"}"
|
||||
};
|
||||
|
||||
/// @brief Test fixture class for testing 4-way exchange: Solicit-Advertise,
|
||||
/// Request-Reply.
|
||||
class InfRequestTest : public Dhcpv6SrvTest {
|
||||
public:
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// Sets up fake interfaces.
|
||||
InfRequestTest()
|
||||
: Dhcpv6SrvTest(),
|
||||
iface_mgr_test_config_(true) {
|
||||
}
|
||||
|
||||
/// @brief Interface Manager's fake configuration control.
|
||||
IfaceMgrTestConfig iface_mgr_test_config_;
|
||||
};
|
||||
|
||||
/// Check that server processes correctly an incoming inf-request in a
|
||||
/// typical subnet that has also address and prefix pools.
|
||||
TEST_F(InfRequestTest, infRequestBasic) {
|
||||
Dhcp6Client client;
|
||||
|
||||
// Configure client to request IA_PD.
|
||||
configure(CONFIGS[0], *client.getServer());
|
||||
// Make sure we ended-up having expected number of subnets configured.
|
||||
const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()->
|
||||
getCfgSubnets6()->getAll();
|
||||
ASSERT_EQ(1, subnets->size());
|
||||
|
||||
// Perform 2-way exchange (Inf-request/reply)
|
||||
client.requestOption(D6O_NAME_SERVERS);
|
||||
ASSERT_NO_THROW(client.doInfRequest());
|
||||
|
||||
// Confirm that there's a response
|
||||
Pkt6Ptr response = client.getContext().response_;
|
||||
ASSERT_TRUE(response);
|
||||
|
||||
// Check that it contains our client-id
|
||||
OptionPtr client_id = response->getOption(D6O_CLIENTID);
|
||||
ASSERT_TRUE(client_id);
|
||||
EXPECT_TRUE(compareOptions(client_id, client.getClientId()));
|
||||
|
||||
// Check that it contains proper server-id
|
||||
OptionPtr server_id = response->getOption(D6O_SERVERID);
|
||||
ASSERT_TRUE(server_id);
|
||||
EXPECT_TRUE(compareOptions(server_id, client.getServer()->getServerID()));
|
||||
|
||||
// Check that we received requested DNS servers option
|
||||
Option6AddrLstPtr dns = boost::dynamic_pointer_cast<Option6AddrLst>
|
||||
(response->getOption(D6O_NAME_SERVERS));
|
||||
ASSERT_TRUE(dns);
|
||||
Option6AddrLst::AddressContainer addrs = dns->getAddresses();
|
||||
ASSERT_EQ(2, addrs.size());
|
||||
EXPECT_EQ("2001:db8::1", addrs[0].toText());
|
||||
EXPECT_EQ("2001:db8::2", addrs[1].toText());
|
||||
}
|
||||
|
||||
/// Check that server processes correctly an incoming inf-request
|
||||
/// that does not hold client-id. It's so called anonymous inf-request.
|
||||
/// Uncommon, but certainly valid behavior.
|
||||
TEST_F(InfRequestTest, infRequestAnonymous) {
|
||||
Dhcp6Client client;
|
||||
|
||||
// Configure client to request IA_PD.
|
||||
configure(CONFIGS[0], *client.getServer());
|
||||
// Make sure we ended-up having expected number of subnets configured.
|
||||
const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()->
|
||||
getCfgSubnets6()->getAll();
|
||||
ASSERT_EQ(1, subnets->size());
|
||||
|
||||
// Perform 2-way exchange (Inf-request/reply)
|
||||
client.requestOption(D6O_NAME_SERVERS);
|
||||
client.useClientId(false);
|
||||
ASSERT_NO_THROW(client.doInfRequest());
|
||||
|
||||
// Confirm that there's a response
|
||||
Pkt6Ptr response = client.getContext().response_;
|
||||
ASSERT_TRUE(response);
|
||||
|
||||
// Check that we received the requested DNS servers option
|
||||
Option6AddrLstPtr dns = boost::dynamic_pointer_cast<Option6AddrLst>
|
||||
(response->getOption(D6O_NAME_SERVERS));
|
||||
ASSERT_TRUE(dns);
|
||||
Option6AddrLst::AddressContainer addrs = dns->getAddresses();
|
||||
ASSERT_EQ(2, addrs.size());
|
||||
EXPECT_EQ("2001:db8::1", addrs[0].toText());
|
||||
EXPECT_EQ("2001:db8::2", addrs[1].toText());
|
||||
}
|
||||
|
||||
/// Check that server processes correctly an incoming inf-request
|
||||
/// if there is a subnet without any addresses or prefixes configured.
|
||||
TEST_F(InfRequestTest, infRequestStateless) {
|
||||
Dhcp6Client client;
|
||||
|
||||
// Configure client to request IA_PD.
|
||||
configure(CONFIGS[1], *client.getServer());
|
||||
// Make sure we ended-up having expected number of subnets configured.
|
||||
const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()->
|
||||
getCfgSubnets6()->getAll();
|
||||
ASSERT_EQ(1, subnets->size());
|
||||
|
||||
// Perform 2-way exchange (Inf-request/reply)
|
||||
client.requestOption(D6O_SIP_SERVERS_ADDR);
|
||||
ASSERT_NO_THROW(client.doInfRequest());
|
||||
|
||||
// Confirm that there's a response
|
||||
Pkt6Ptr response = client.getContext().response_;
|
||||
ASSERT_TRUE(response);
|
||||
|
||||
// Check that we received the requested SIP servers option
|
||||
Option6AddrLstPtr sip = boost::dynamic_pointer_cast<Option6AddrLst>
|
||||
(response->getOption(D6O_SIP_SERVERS_ADDR));
|
||||
ASSERT_TRUE(sip);
|
||||
Option6AddrLst::AddressContainer addrs = sip->getAddresses();
|
||||
ASSERT_EQ(1, addrs.size());
|
||||
EXPECT_EQ("2001:db8::abcd", addrs[0].toText());
|
||||
}
|
||||
|
||||
/// Check that server processes correctly an incoming inf-request
|
||||
/// if there are options defined at both global and subnet scope.
|
||||
TEST_F(InfRequestTest, infRequestSubnetAndGlobal) {
|
||||
Dhcp6Client client;
|
||||
|
||||
// Configure client to request IA_PD.
|
||||
configure(CONFIGS[2], *client.getServer());
|
||||
// Make sure we ended-up having expected number of subnets configured.
|
||||
const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()->
|
||||
getCfgSubnets6()->getAll();
|
||||
ASSERT_EQ(1, subnets->size());
|
||||
|
||||
// Perform 2-way exchange (Inf-request/reply)
|
||||
client.requestOption(D6O_SIP_SERVERS_ADDR);
|
||||
client.requestOption(D6O_NAME_SERVERS);
|
||||
ASSERT_NO_THROW(client.doInfRequest());
|
||||
|
||||
// Confirm that there's a response
|
||||
Pkt6Ptr response = client.getContext().response_;
|
||||
ASSERT_TRUE(response);
|
||||
|
||||
// Check that we received the requested sip servers option
|
||||
Option6AddrLstPtr sip = boost::dynamic_pointer_cast<Option6AddrLst>
|
||||
(response->getOption(D6O_SIP_SERVERS_ADDR));
|
||||
ASSERT_TRUE(sip);
|
||||
Option6AddrLst::AddressContainer addrs = sip->getAddresses();
|
||||
ASSERT_EQ(1, addrs.size());
|
||||
EXPECT_EQ("2001:db8::1", addrs[0].toText());
|
||||
|
||||
// Check that we received the requested dns servers option
|
||||
Option6AddrLstPtr dns = boost::dynamic_pointer_cast<Option6AddrLst>
|
||||
(response->getOption(D6O_NAME_SERVERS));
|
||||
ASSERT_TRUE(dns);
|
||||
addrs = dns->getAddresses();
|
||||
ASSERT_EQ(1, addrs.size());
|
||||
EXPECT_EQ("2001:db8::2", addrs[0].toText());
|
||||
}
|
||||
|
||||
/// Check that server processes correctly an incoming inf-request
|
||||
/// if there are options defined at global scope only (no subnets).
|
||||
TEST_F(InfRequestTest, infRequestNoSubnets) {
|
||||
Dhcp6Client client;
|
||||
|
||||
// Configure client to request IA_PD.
|
||||
configure(CONFIGS[3], *client.getServer());
|
||||
// Make sure we ended-up having expected number of subnets configured.
|
||||
const Subnet6Collection* subnets = CfgMgr::instance().getCurrentCfg()->
|
||||
getCfgSubnets6()->getAll();
|
||||
ASSERT_EQ(0, subnets->size());
|
||||
|
||||
// Perform 2-way exchange (Inf-request/reply)
|
||||
client.requestOption(D6O_NIS_SERVERS);
|
||||
ASSERT_NO_THROW(client.doInfRequest());
|
||||
|
||||
// Confirm that there's a response
|
||||
Pkt6Ptr response = client.getContext().response_;
|
||||
ASSERT_TRUE(response);
|
||||
|
||||
// Check that we received the requested sip servers option
|
||||
Option6AddrLstPtr nis = boost::dynamic_pointer_cast<Option6AddrLst>
|
||||
(response->getOption(D6O_NIS_SERVERS));
|
||||
ASSERT_TRUE(nis);
|
||||
Option6AddrLst::AddressContainer addrs = nis->getAddresses();
|
||||
ASSERT_EQ(2, addrs.size());
|
||||
EXPECT_EQ("2001:db8::1", addrs[0].toText());
|
||||
EXPECT_EQ("2001:db8::2", addrs[1].toText());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
} // end of anonymous namespace
|
Reference in New Issue
Block a user