mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-03 15:35:17 +00:00
[3220] Moved DHCPv6 Client FQDN specific tests to a separate file.
This commit is contained in:
@@ -69,6 +69,7 @@ libco2_la_LDFLAGS = -avoid-version -export-dynamic -module
|
|||||||
TESTS += dhcp6_unittests
|
TESTS += dhcp6_unittests
|
||||||
dhcp6_unittests_SOURCES = dhcp6_unittests.cc
|
dhcp6_unittests_SOURCES = dhcp6_unittests.cc
|
||||||
dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
|
dhcp6_unittests_SOURCES += dhcp6_srv_unittest.cc
|
||||||
|
dhcp6_unittests_SOURCES += fqdn_unittest.cc
|
||||||
dhcp6_unittests_SOURCES += hooks_unittest.cc
|
dhcp6_unittests_SOURCES += hooks_unittest.cc
|
||||||
dhcp6_unittests_SOURCES += dhcp6_test_utils.cc dhcp6_test_utils.h
|
dhcp6_unittests_SOURCES += dhcp6_test_utils.cc dhcp6_test_utils.h
|
||||||
dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
|
dhcp6_unittests_SOURCES += ctrl_dhcp6_srv_unittest.cc
|
||||||
|
@@ -15,19 +15,16 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
#include <asiolink/io_address.h>
|
#include <asiolink/io_address.h>
|
||||||
#include <dhcp_ddns/ncr_msg.h>
|
|
||||||
#include <dhcp/dhcp6.h>
|
#include <dhcp/dhcp6.h>
|
||||||
#include <dhcp/duid.h>
|
#include <dhcp/duid.h>
|
||||||
#include <dhcp/option.h>
|
#include <dhcp/option.h>
|
||||||
#include <dhcp/option_custom.h>
|
|
||||||
#include <dhcp/option6_addrlst.h>
|
#include <dhcp/option6_addrlst.h>
|
||||||
#include <dhcp/option6_client_fqdn.h>
|
|
||||||
#include <dhcp/option6_ia.h>
|
#include <dhcp/option6_ia.h>
|
||||||
#include <dhcp/option6_iaaddr.h>
|
#include <dhcp/option6_iaaddr.h>
|
||||||
#include <dhcp/option_int.h>
|
#include <dhcp/option_int.h>
|
||||||
#include <dhcp/option_vendor.h>
|
|
||||||
#include <dhcp/option_int_array.h>
|
#include <dhcp/option_int_array.h>
|
||||||
#include <dhcp/option_string.h>
|
#include <dhcp/option_string.h>
|
||||||
|
#include <dhcp/option_vendor.h>
|
||||||
#include <dhcp/iface_mgr.h>
|
#include <dhcp/iface_mgr.h>
|
||||||
#include <dhcp6/config_parser.h>
|
#include <dhcp6/config_parser.h>
|
||||||
#include <dhcp/dhcp6.h>
|
#include <dhcp/dhcp6.h>
|
||||||
@@ -53,267 +50,12 @@ using namespace isc;
|
|||||||
using namespace isc::test;
|
using namespace isc::test;
|
||||||
using namespace isc::asiolink;
|
using namespace isc::asiolink;
|
||||||
using namespace isc::dhcp;
|
using namespace isc::dhcp;
|
||||||
using namespace isc::dhcp_ddns;
|
|
||||||
using namespace isc::util;
|
using namespace isc::util;
|
||||||
using namespace isc::hooks;
|
using namespace isc::hooks;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
// namespace has to be named, because friends are defined in Dhcpv6Srv class
|
|
||||||
// Maybe it should be isc::test?
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// This is a test fixture class for testing the processing of the DHCPv6 Client
|
|
||||||
// FQDN Option.
|
|
||||||
class FqdnDhcpv6SrvTest : public Dhcpv6SrvTest {
|
|
||||||
public:
|
|
||||||
// Constructor
|
|
||||||
FqdnDhcpv6SrvTest()
|
|
||||||
: Dhcpv6SrvTest() {
|
|
||||||
// generateClientId assigns DUID to duid_.
|
|
||||||
generateClientId();
|
|
||||||
lease_.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
|
|
||||||
duid_, 1234, 501, 502, 503,
|
|
||||||
504, 1, 0));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destructor
|
|
||||||
virtual ~FqdnDhcpv6SrvTest() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the DHCPv6 Client FQDN Option using flags and domain-name.
|
|
||||||
Option6ClientFqdnPtr
|
|
||||||
createClientFqdn(const uint8_t flags,
|
|
||||||
const std::string& fqdn_name,
|
|
||||||
const Option6ClientFqdn::DomainNameType fqdn_type) {
|
|
||||||
return (Option6ClientFqdnPtr(new Option6ClientFqdn(flags,
|
|
||||||
fqdn_name,
|
|
||||||
fqdn_type)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a message which optionally holds DHCPv6 Client FQDN Option.
|
|
||||||
Pkt6Ptr generateMessage(uint8_t msg_type,
|
|
||||||
const uint8_t fqdn_flags,
|
|
||||||
const std::string& fqdn_domain_name,
|
|
||||||
const Option6ClientFqdn::DomainNameType
|
|
||||||
fqdn_type,
|
|
||||||
const bool include_oro,
|
|
||||||
const bool include_fqdn = true,
|
|
||||||
OptionPtr srvid = OptionPtr()) {
|
|
||||||
Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
|
|
||||||
pkt->setRemoteAddr(IOAddress("fe80::abcd"));
|
|
||||||
Option6IAPtr ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
|
|
||||||
|
|
||||||
if (msg_type != DHCPV6_REPLY) {
|
|
||||||
IOAddress hint("2001:db8:1:1::dead:beef");
|
|
||||||
OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500));
|
|
||||||
ia->addOption(hint_opt);
|
|
||||||
pkt->addOption(ia);
|
|
||||||
}
|
|
||||||
|
|
||||||
OptionPtr clientid = generateClientId();
|
|
||||||
pkt->addOption(clientid);
|
|
||||||
if (srvid && (msg_type != DHCPV6_SOLICIT)) {
|
|
||||||
pkt->addOption(srvid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (include_fqdn) {
|
|
||||||
pkt->addOption(createClientFqdn(fqdn_flags, fqdn_domain_name,
|
|
||||||
fqdn_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (include_oro) {
|
|
||||||
OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6,
|
|
||||||
D6O_ORO));
|
|
||||||
oro->addValue(D6O_CLIENT_FQDN);
|
|
||||||
pkt->addOption(oro);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (pkt);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates instance of the DHCPv6 message with client id and server id.
|
|
||||||
Pkt6Ptr generateMessageWithIds(const uint8_t msg_type,
|
|
||||||
NakedDhcpv6Srv& srv) {
|
|
||||||
Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
|
|
||||||
// Generate client-id.
|
|
||||||
OptionPtr opt_clientid = generateClientId();
|
|
||||||
pkt->addOption(opt_clientid);
|
|
||||||
|
|
||||||
if (msg_type != DHCPV6_SOLICIT) {
|
|
||||||
// Generate server-id.
|
|
||||||
pkt->addOption(srv.getServerID());
|
|
||||||
}
|
|
||||||
|
|
||||||
return (pkt);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns an instance of the option carrying FQDN.
|
|
||||||
Option6ClientFqdnPtr getClientFqdnOption(const Pkt6Ptr& pkt) {
|
|
||||||
return (boost::dynamic_pointer_cast<Option6ClientFqdn>
|
|
||||||
(pkt->getOption(D6O_CLIENT_FQDN)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds IA option to the message. Option holds an address.
|
|
||||||
void addIA(const uint32_t iaid, const IOAddress& addr, Pkt6Ptr& pkt) {
|
|
||||||
Option6IAPtr opt_ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
|
|
||||||
Option6IAAddrPtr opt_iaaddr(new Option6IAAddr(D6O_IAADDR, addr,
|
|
||||||
300, 500));
|
|
||||||
opt_ia->addOption(opt_iaaddr);
|
|
||||||
pkt->addOption(opt_ia);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds IA option to the message. Option holds status code.
|
|
||||||
void addIA(const uint32_t iaid, const uint16_t status_code, Pkt6Ptr& pkt) {
|
|
||||||
Option6IAPtr opt_ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
|
|
||||||
addStatusCode(status_code, "", opt_ia);
|
|
||||||
pkt->addOption(opt_ia);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates status code with the specified code and message.
|
|
||||||
OptionCustomPtr createStatusCode(const uint16_t code,
|
|
||||||
const std::string& msg) {
|
|
||||||
OptionDefinition def("status-code", D6O_STATUS_CODE, "record");
|
|
||||||
def.addRecordField("uint16");
|
|
||||||
def.addRecordField("string");
|
|
||||||
OptionCustomPtr opt_status(new OptionCustom(def, Option::V6));
|
|
||||||
opt_status->writeInteger(code);
|
|
||||||
if (!msg.empty()) {
|
|
||||||
opt_status->writeString(msg, 1);
|
|
||||||
}
|
|
||||||
return (opt_status);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds Status Code option to the IA.
|
|
||||||
void addStatusCode(const uint16_t code, const std::string& msg,
|
|
||||||
Option6IAPtr& opt_ia) {
|
|
||||||
opt_ia->addOption(createStatusCode(code, msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test processing of the DHCPv6 Client FQDN Option.
|
|
||||||
void testFqdn(const uint16_t msg_type,
|
|
||||||
const bool use_oro,
|
|
||||||
const uint8_t in_flags,
|
|
||||||
const std::string& in_domain_name,
|
|
||||||
const Option6ClientFqdn::DomainNameType in_domain_type,
|
|
||||||
const uint8_t exp_flags,
|
|
||||||
const std::string& exp_domain_name) {
|
|
||||||
NakedDhcpv6Srv srv(0);
|
|
||||||
Pkt6Ptr question = generateMessage(msg_type,
|
|
||||||
in_flags,
|
|
||||||
in_domain_name,
|
|
||||||
in_domain_type,
|
|
||||||
use_oro);
|
|
||||||
ASSERT_TRUE(getClientFqdnOption(question));
|
|
||||||
|
|
||||||
Option6ClientFqdnPtr answ_fqdn;
|
|
||||||
ASSERT_NO_THROW(answ_fqdn = srv.processClientFqdn(question));
|
|
||||||
ASSERT_TRUE(answ_fqdn);
|
|
||||||
|
|
||||||
const bool flag_n = (exp_flags & Option6ClientFqdn::FLAG_N) != 0;
|
|
||||||
const bool flag_s = (exp_flags & Option6ClientFqdn::FLAG_S) != 0;
|
|
||||||
const bool flag_o = (exp_flags & Option6ClientFqdn::FLAG_O) != 0;
|
|
||||||
|
|
||||||
EXPECT_EQ(flag_n, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_N));
|
|
||||||
EXPECT_EQ(flag_s, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_S));
|
|
||||||
EXPECT_EQ(flag_o, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_O));
|
|
||||||
|
|
||||||
EXPECT_EQ(exp_domain_name, answ_fqdn->getDomainName());
|
|
||||||
EXPECT_EQ(Option6ClientFqdn::FULL, answ_fqdn->getDomainNameType());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that the client message holding an FQDN is processed and the
|
|
||||||
// lease is acquired.
|
|
||||||
void testProcessMessage(const uint8_t msg_type,
|
|
||||||
const std::string& hostname,
|
|
||||||
NakedDhcpv6Srv& srv,
|
|
||||||
const bool test_fqdn = true) {
|
|
||||||
// Create a message of a specified type, add server id and
|
|
||||||
// FQDN option.
|
|
||||||
OptionPtr srvid = srv.getServerID();
|
|
||||||
Pkt6Ptr req = generateMessage(msg_type, Option6ClientFqdn::FLAG_S,
|
|
||||||
hostname,
|
|
||||||
Option6ClientFqdn::FULL,
|
|
||||||
true, test_fqdn, srvid);
|
|
||||||
|
|
||||||
// For different client's message types we have to invoke different
|
|
||||||
// functions to generate response.
|
|
||||||
Pkt6Ptr reply;
|
|
||||||
if (msg_type == DHCPV6_SOLICIT) {
|
|
||||||
ASSERT_NO_THROW(reply = srv.processSolicit(req));
|
|
||||||
|
|
||||||
} else if (msg_type == DHCPV6_REQUEST) {
|
|
||||||
ASSERT_NO_THROW(reply = srv.processRequest(req));
|
|
||||||
|
|
||||||
} else if (msg_type == DHCPV6_RENEW) {
|
|
||||||
ASSERT_NO_THROW(reply = srv.processRequest(req));
|
|
||||||
|
|
||||||
} else if (msg_type == DHCPV6_RELEASE) {
|
|
||||||
// For Release no lease will be acquired so we have to leave
|
|
||||||
// function here.
|
|
||||||
ASSERT_NO_THROW(reply = srv.processRelease(req));
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
// We are not interested in testing other message types.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For Solicit, we will get different message type obviously.
|
|
||||||
if (msg_type == DHCPV6_SOLICIT) {
|
|
||||||
checkResponse(reply, DHCPV6_ADVERTISE, 1234);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
checkResponse(reply, DHCPV6_REPLY, 1234);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check verify that IA_NA is correct.
|
|
||||||
Option6IAAddrPtr addr =
|
|
||||||
checkIA_NA(reply, 234, subnet_->getT1(), subnet_->getT2());
|
|
||||||
ASSERT_TRUE(addr);
|
|
||||||
|
|
||||||
// Check that we have got the address we requested.
|
|
||||||
checkIAAddr(addr, IOAddress("2001:db8:1:1::dead:beef"),
|
|
||||||
Lease::TYPE_NA);
|
|
||||||
|
|
||||||
if (msg_type != DHCPV6_SOLICIT) {
|
|
||||||
// Check that the lease exists.
|
|
||||||
Lease6Ptr lease =
|
|
||||||
checkLease(duid_, reply->getOption(D6O_IA_NA), addr);
|
|
||||||
ASSERT_TRUE(lease);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (test_fqdn) {
|
|
||||||
ASSERT_TRUE(reply->getOption(D6O_CLIENT_FQDN));
|
|
||||||
} else {
|
|
||||||
ASSERT_FALSE(reply->getOption(D6O_CLIENT_FQDN));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that NameChangeRequest holds valid values.
|
|
||||||
void verifyNameChangeRequest(NakedDhcpv6Srv& srv,
|
|
||||||
const isc::dhcp_ddns::NameChangeType type,
|
|
||||||
const bool reverse, const bool forward,
|
|
||||||
const std::string& addr,
|
|
||||||
const std::string& dhcid,
|
|
||||||
const uint16_t expires,
|
|
||||||
const uint16_t len) {
|
|
||||||
NameChangeRequest ncr = srv.name_change_reqs_.front();
|
|
||||||
EXPECT_EQ(type, ncr.getChangeType());
|
|
||||||
EXPECT_EQ(forward, ncr.isForwardChange());
|
|
||||||
EXPECT_EQ(reverse, ncr.isReverseChange());
|
|
||||||
EXPECT_EQ(addr, ncr.getIpAddress());
|
|
||||||
EXPECT_EQ(dhcid, ncr.getDhcid().toStr());
|
|
||||||
EXPECT_EQ(expires, ncr.getLeaseExpiresOn());
|
|
||||||
EXPECT_EQ(len, ncr.getLeaseLength());
|
|
||||||
EXPECT_EQ(isc::dhcp_ddns::ST_NEW, ncr.getStatus());
|
|
||||||
srv.name_change_reqs_.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Holds a lease used by a test.
|
|
||||||
Lease6Ptr lease_;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// This test verifies that incoming SOLICIT can be handled properly when
|
// This test verifies that incoming SOLICIT can be handled properly when
|
||||||
// there are no subnets configured.
|
// there are no subnets configured.
|
||||||
//
|
//
|
||||||
@@ -1584,390 +1326,6 @@ TEST_F(Dhcpv6SrvTest, ServerID) {
|
|||||||
EXPECT_EQ(duid1_text, text);
|
EXPECT_EQ(duid1_text, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A set of tests verifying server's behaviour when it receives the DHCPv6
|
|
||||||
// Client Fqdn Option.
|
|
||||||
// @todo: Extend these tests once appropriate configuration parameters are
|
|
||||||
// implemented (ticket #3034).
|
|
||||||
|
|
||||||
// Test server's response when client requests that server performs AAAA update.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdate) {
|
|
||||||
testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S,
|
|
||||||
"myhost.example.com",
|
|
||||||
Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_S,
|
|
||||||
"myhost.example.com.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test server's response when client provides partial domain-name and requests
|
|
||||||
// that server performs AAAA update.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdatePartialName) {
|
|
||||||
testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S, "myhost",
|
|
||||||
Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
|
|
||||||
"myhost.example.com.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test server's response when client provides empty domain-name and requests
|
|
||||||
// that server performs AAAA update.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdateNoName) {
|
|
||||||
testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S, "",
|
|
||||||
Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
|
|
||||||
"myhost.example.com.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test server's response when client requests no DNS update.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, noUpdate) {
|
|
||||||
testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_N,
|
|
||||||
"myhost.example.com",
|
|
||||||
Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_N,
|
|
||||||
"myhost.example.com.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test server's response when client requests that server delegates the AAAA
|
|
||||||
// update to the client and this delegation is not allowed.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, clientAAAAUpdateNotAllowed) {
|
|
||||||
testFqdn(DHCPV6_SOLICIT, true, 0, "myhost.example.com.",
|
|
||||||
Option6ClientFqdn::FULL,
|
|
||||||
Option6ClientFqdn::FLAG_S | Option6ClientFqdn::FLAG_O,
|
|
||||||
"myhost.example.com.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that exception is thrown if supplied NULL answer packet when
|
|
||||||
// creating NameChangeRequests.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAnswer) {
|
|
||||||
NakedDhcpv6Srv srv(0);
|
|
||||||
|
|
||||||
Pkt6Ptr answer;
|
|
||||||
Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
|
|
||||||
"myhost.example.com",
|
|
||||||
Option6ClientFqdn::FULL);
|
|
||||||
EXPECT_THROW(srv.createNameChangeRequests(answer, fqdn),
|
|
||||||
isc::Unexpected);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that exception is thrown if supplied answer from the server
|
|
||||||
// contains no DUID when creating NameChangeRequests.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoDUID) {
|
|
||||||
NakedDhcpv6Srv srv(0);
|
|
||||||
|
|
||||||
Pkt6Ptr answer = Pkt6Ptr(new Pkt6(DHCPV6_REPLY, 1234));
|
|
||||||
Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
|
|
||||||
"myhost.example.com",
|
|
||||||
Option6ClientFqdn::FULL);
|
|
||||||
|
|
||||||
EXPECT_THROW(srv.createNameChangeRequests(answer, fqdn),
|
|
||||||
isc::Unexpected);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test no NameChangeRequests are added if FQDN option is NULL.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoFQDN) {
|
|
||||||
NakedDhcpv6Srv srv(0);
|
|
||||||
|
|
||||||
// Create Reply message with Client Id and Server id.
|
|
||||||
Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
|
|
||||||
|
|
||||||
// Pass NULL FQDN option. No NameChangeRequests should be created.
|
|
||||||
Option6ClientFqdnPtr fqdn;
|
|
||||||
ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
|
|
||||||
|
|
||||||
// There should be no new NameChangeRequests.
|
|
||||||
EXPECT_TRUE(srv.name_change_reqs_.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that NameChangeRequests are not generated if an answer message
|
|
||||||
// contains no addresses.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAddr) {
|
|
||||||
NakedDhcpv6Srv srv(0);
|
|
||||||
|
|
||||||
// Create Reply message with Client Id and Server id.
|
|
||||||
Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
|
|
||||||
|
|
||||||
Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
|
|
||||||
"myhost.example.com",
|
|
||||||
Option6ClientFqdn::FULL);
|
|
||||||
|
|
||||||
ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
|
|
||||||
|
|
||||||
// We didn't add any IAs, so there should be no NameChangeRequests in th
|
|
||||||
// queue.
|
|
||||||
ASSERT_TRUE(srv.name_change_reqs_.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that a number of NameChangeRequests is created as a result of
|
|
||||||
// processing the answer message which holds 3 IAs and when FQDN is
|
|
||||||
// specified.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequests) {
|
|
||||||
NakedDhcpv6Srv srv(0);
|
|
||||||
|
|
||||||
// Create Reply message with Client Id and Server id.
|
|
||||||
Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
|
|
||||||
|
|
||||||
// Create three IAs, each having different address.
|
|
||||||
addIA(1234, IOAddress("2001:db8:1::1"), answer);
|
|
||||||
addIA(2345, IOAddress("2001:db8:1::2"), answer);
|
|
||||||
addIA(3456, IOAddress("2001:db8:1::3"), answer);
|
|
||||||
|
|
||||||
// Use domain name in upper case. It should be converted to lower-case
|
|
||||||
// before DHCID is calculated. So, we should get the same result as if
|
|
||||||
// we typed domain name in lower-case.
|
|
||||||
Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
|
|
||||||
"MYHOST.EXAMPLE.COM",
|
|
||||||
Option6ClientFqdn::FULL);
|
|
||||||
|
|
||||||
// Create NameChangeRequests. Since we have added 3 IAs, it should
|
|
||||||
// result in generation of 3 distinct NameChangeRequests.
|
|
||||||
ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
|
|
||||||
ASSERT_EQ(3, srv.name_change_reqs_.size());
|
|
||||||
|
|
||||||
// Verify that NameChangeRequests are correct. Each call to the
|
|
||||||
// verifyNameChangeRequest will pop verified request from the queue.
|
|
||||||
|
|
||||||
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
|
|
||||||
"2001:db8:1::1",
|
|
||||||
"000201415AA33D1187D148275136FA30300478"
|
|
||||||
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
|
||||||
0, 500);
|
|
||||||
|
|
||||||
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
|
|
||||||
"2001:db8:1::2",
|
|
||||||
"000201415AA33D1187D148275136FA30300478"
|
|
||||||
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
|
||||||
0, 500);
|
|
||||||
|
|
||||||
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
|
|
||||||
"2001:db8:1::3",
|
|
||||||
"000201415AA33D1187D148275136FA30300478"
|
|
||||||
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
|
||||||
0, 500);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test creation of the NameChangeRequest to remove both forward and reverse
|
|
||||||
// mapping for the given lease.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestFwdRev) {
|
|
||||||
NakedDhcpv6Srv srv(0);
|
|
||||||
|
|
||||||
lease_->fqdn_fwd_ = true;
|
|
||||||
lease_->fqdn_rev_ = true;
|
|
||||||
// Part of the domain name is in upper case, to test that it gets converted
|
|
||||||
// to lower case before DHCID is computed. So, we should get the same DHCID
|
|
||||||
// as if we typed domain-name in lower case.
|
|
||||||
lease_->hostname_ = "MYHOST.example.com.";
|
|
||||||
|
|
||||||
ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
|
|
||||||
|
|
||||||
ASSERT_EQ(1, srv.name_change_reqs_.size());
|
|
||||||
|
|
||||||
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
|
|
||||||
"2001:db8:1::1",
|
|
||||||
"000201415AA33D1187D148275136FA30300478"
|
|
||||||
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
|
||||||
0, 502);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test creation of the NameChangeRequest to remove reverse mapping for the
|
|
||||||
// given lease.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestRev) {
|
|
||||||
NakedDhcpv6Srv srv(0);
|
|
||||||
|
|
||||||
lease_->fqdn_fwd_ = false;
|
|
||||||
lease_->fqdn_rev_ = true;
|
|
||||||
lease_->hostname_ = "myhost.example.com.";
|
|
||||||
|
|
||||||
ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
|
|
||||||
|
|
||||||
ASSERT_EQ(1, srv.name_change_reqs_.size());
|
|
||||||
|
|
||||||
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, false,
|
|
||||||
"2001:db8:1::1",
|
|
||||||
"000201415AA33D1187D148275136FA30300478"
|
|
||||||
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
|
||||||
0, 502);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that NameChangeRequest to remove DNS records is not generated when
|
|
||||||
// neither forward nor reverse DNS update has been performed for a lease.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoUpdate) {
|
|
||||||
NakedDhcpv6Srv srv(0);
|
|
||||||
|
|
||||||
lease_->fqdn_fwd_ = false;
|
|
||||||
lease_->fqdn_rev_ = false;
|
|
||||||
|
|
||||||
ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
|
|
||||||
|
|
||||||
EXPECT_TRUE(srv.name_change_reqs_.empty());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that NameChangeRequest is not generated if the hostname hasn't been
|
|
||||||
// specified for a lease for which forward and reverse mapping has been set.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoHostname) {
|
|
||||||
NakedDhcpv6Srv srv(0);
|
|
||||||
|
|
||||||
lease_->fqdn_fwd_ = true;
|
|
||||||
lease_->fqdn_rev_ = true;
|
|
||||||
lease_->hostname_ = "";
|
|
||||||
|
|
||||||
ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
|
|
||||||
|
|
||||||
EXPECT_TRUE(srv.name_change_reqs_.empty());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that NameChangeRequest is not generated if the invalid hostname has
|
|
||||||
// been specified for a lease for which forward and reverse mapping has been
|
|
||||||
// set.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestWrongHostname) {
|
|
||||||
NakedDhcpv6Srv srv(0);
|
|
||||||
|
|
||||||
lease_->fqdn_fwd_ = true;
|
|
||||||
lease_->fqdn_rev_ = true;
|
|
||||||
lease_->hostname_ = "myhost..example.com.";
|
|
||||||
|
|
||||||
ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
|
|
||||||
|
|
||||||
EXPECT_TRUE(srv.name_change_reqs_.empty());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that Advertise message generated in a response to the Solicit will
|
|
||||||
// not result in generation if the NameChangeRequests.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, processSolicit) {
|
|
||||||
NakedDhcpv6Srv srv(0);
|
|
||||||
|
|
||||||
// Create a Solicit message with FQDN option and generate server's
|
|
||||||
// response using processSolicit function.
|
|
||||||
testProcessMessage(DHCPV6_SOLICIT, "myhost.example.com", srv);
|
|
||||||
EXPECT_TRUE(srv.name_change_reqs_.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that client may send two requests, each carrying FQDN option with
|
|
||||||
// a different domain-name. Server should use existing lease for the second
|
|
||||||
// request but modify the DNS entries for the lease according to the contents
|
|
||||||
// of the FQDN sent in the second request.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, processTwoRequests) {
|
|
||||||
NakedDhcpv6Srv srv(0);
|
|
||||||
|
|
||||||
// Create a Request message with FQDN option and generate server's
|
|
||||||
// response using processRequest function. This will result in the
|
|
||||||
// creation of a new lease and the appropriate NameChangeRequest
|
|
||||||
// to add both reverse and forward mapping to DNS.
|
|
||||||
testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
|
|
||||||
ASSERT_EQ(1, srv.name_change_reqs_.size());
|
|
||||||
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
|
|
||||||
"2001:db8:1:1::dead:beef",
|
|
||||||
"000201415AA33D1187D148275136FA30300478"
|
|
||||||
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
|
||||||
0, 4000);
|
|
||||||
|
|
||||||
// Client may send another request message with a new domain-name. In this
|
|
||||||
// case the same lease will be returned. The existing DNS entry needs to
|
|
||||||
// be replaced with a new one. Server should determine that the different
|
|
||||||
// FQDN has been already added to the DNS. As a result, the old DNS
|
|
||||||
// entries should be removed and the entries for the new domain-name
|
|
||||||
// should be added. Therefore, we expect two NameChangeRequests. One to
|
|
||||||
// remove the existing entries, one to add new entries.
|
|
||||||
testProcessMessage(DHCPV6_REQUEST, "otherhost.example.com", srv);
|
|
||||||
ASSERT_EQ(2, srv.name_change_reqs_.size());
|
|
||||||
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
|
|
||||||
"2001:db8:1:1::dead:beef",
|
|
||||||
"000201415AA33D1187D148275136FA30300478"
|
|
||||||
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
|
||||||
0, 4000);
|
|
||||||
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
|
|
||||||
"2001:db8:1:1::dead:beef",
|
|
||||||
"000201D422AA463306223D269B6CB7AFE7AAD265FC"
|
|
||||||
"EA97F93623019B2E0D14E5323D5A",
|
|
||||||
0, 4000);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that client may send Request followed by the Renew, both holding
|
|
||||||
// FQDN options, but each option holding different domain-name. The Renew
|
|
||||||
// should result in generation of the two NameChangeRequests, one to remove
|
|
||||||
// DNS entry added previously when Request was processed, another one to
|
|
||||||
// add a new entry for the FQDN held in the Renew.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, processRequestRenew) {
|
|
||||||
NakedDhcpv6Srv srv(0);
|
|
||||||
|
|
||||||
// Create a Request message with FQDN option and generate server's
|
|
||||||
// response using processRequest function. This will result in the
|
|
||||||
// creation of a new lease and the appropriate NameChangeRequest
|
|
||||||
// to add both reverse and forward mapping to DNS.
|
|
||||||
testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
|
|
||||||
ASSERT_EQ(1, srv.name_change_reqs_.size());
|
|
||||||
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
|
|
||||||
"2001:db8:1:1::dead:beef",
|
|
||||||
"000201415AA33D1187D148275136FA30300478"
|
|
||||||
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
|
||||||
0, 4000);
|
|
||||||
|
|
||||||
// Client may send Renew message with a new domain-name. In this
|
|
||||||
// case the same lease will be returned. The existing DNS entry needs to
|
|
||||||
// be replaced with a new one. Server should determine that the different
|
|
||||||
// FQDN has been already added to the DNS. As a result, the old DNS
|
|
||||||
// entries should be removed and the entries for the new domain-name
|
|
||||||
// should be added. Therefore, we expect two NameChangeRequests. One to
|
|
||||||
// remove the existing entries, one to add new entries.
|
|
||||||
testProcessMessage(DHCPV6_RENEW, "otherhost.example.com", srv);
|
|
||||||
ASSERT_EQ(2, srv.name_change_reqs_.size());
|
|
||||||
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
|
|
||||||
"2001:db8:1:1::dead:beef",
|
|
||||||
"000201415AA33D1187D148275136FA30300478"
|
|
||||||
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
|
||||||
0, 4000);
|
|
||||||
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
|
|
||||||
"2001:db8:1:1::dead:beef",
|
|
||||||
"000201D422AA463306223D269B6CB7AFE7AAD265FC"
|
|
||||||
"EA97F93623019B2E0D14E5323D5A",
|
|
||||||
0, 4000);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
|
|
||||||
NakedDhcpv6Srv srv(0);
|
|
||||||
|
|
||||||
// Create a Request message with FQDN option and generate server's
|
|
||||||
// response using processRequest function. This will result in the
|
|
||||||
// creation of a new lease and the appropriate NameChangeRequest
|
|
||||||
// to add both reverse and forward mapping to DNS.
|
|
||||||
testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
|
|
||||||
ASSERT_EQ(1, srv.name_change_reqs_.size());
|
|
||||||
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
|
|
||||||
"2001:db8:1:1::dead:beef",
|
|
||||||
"000201415AA33D1187D148275136FA30300478"
|
|
||||||
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
|
||||||
0, 4000);
|
|
||||||
|
|
||||||
// Client may send Release message. In this case the lease should be
|
|
||||||
// removed and all existing DNS entries for this lease should be
|
|
||||||
// also removed. Therefore, we expect that single NameChangeRequest to
|
|
||||||
// remove DNS entries is generated.
|
|
||||||
testProcessMessage(DHCPV6_RELEASE, "otherhost.example.com", srv);
|
|
||||||
ASSERT_EQ(1, srv.name_change_reqs_.size());
|
|
||||||
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
|
|
||||||
"2001:db8:1:1::dead:beef",
|
|
||||||
"000201415AA33D1187D148275136FA30300478"
|
|
||||||
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
|
||||||
0, 4000);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks that the server does not include DHCPv6 Client FQDN option in its
|
|
||||||
// response when client doesn't include this option in a Request.
|
|
||||||
TEST_F(FqdnDhcpv6SrvTest, processRequestWithoutFqdn) {
|
|
||||||
NakedDhcpv6Srv srv(0);
|
|
||||||
|
|
||||||
// The last parameter disables the use of DHCPv6 Client FQDN option
|
|
||||||
// in the client's Request. In this case, we expect that the FQDN
|
|
||||||
// option will not be included in the server's response. The
|
|
||||||
// testProcessMessage will check that.
|
|
||||||
testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv, false);
|
|
||||||
ASSERT_TRUE(srv.name_change_reqs_.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if server responses are sent to the proper port.
|
// Checks if server responses are sent to the proper port.
|
||||||
TEST_F(Dhcpv6SrvTest, portsDirectTraffic) {
|
TEST_F(Dhcpv6SrvTest, portsDirectTraffic) {
|
||||||
|
|
||||||
|
777
src/bin/dhcp6/tests/fqdn_unittest.cc
Normal file
777
src/bin/dhcp6/tests/fqdn_unittest.cc
Normal file
@@ -0,0 +1,777 @@
|
|||||||
|
// Copyright (C) 2013 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 <asiolink/io_address.h>
|
||||||
|
#include <dhcp_ddns/ncr_msg.h>
|
||||||
|
#include <dhcp/dhcp6.h>
|
||||||
|
#include <dhcp/option.h>
|
||||||
|
#include <dhcp/option_custom.h>
|
||||||
|
#include <dhcp/option6_client_fqdn.h>
|
||||||
|
#include <dhcp/option6_ia.h>
|
||||||
|
#include <dhcp/option6_iaaddr.h>
|
||||||
|
#include <dhcp/option_int_array.h>
|
||||||
|
#include <dhcpsrv/lease.h>
|
||||||
|
|
||||||
|
#include <dhcp6/tests/dhcp6_test_utils.h>
|
||||||
|
#include <boost/pointer_cast.hpp>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
using namespace isc;
|
||||||
|
using namespace isc::test;
|
||||||
|
using namespace isc::asiolink;
|
||||||
|
using namespace isc::dhcp;
|
||||||
|
using namespace isc::dhcp_ddns;
|
||||||
|
using namespace isc::util;
|
||||||
|
using namespace isc::hooks;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
/// @brief A test fixture class for testing DHCPv6 Client FQDN Option handling.
|
||||||
|
class FqdnDhcpv6SrvTest : public Dhcpv6SrvTest {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// @brief Constructor
|
||||||
|
FqdnDhcpv6SrvTest()
|
||||||
|
: Dhcpv6SrvTest() {
|
||||||
|
// generateClientId assigns DUID to duid_.
|
||||||
|
generateClientId();
|
||||||
|
lease_.reset(new Lease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"),
|
||||||
|
duid_, 1234, 501, 502, 503,
|
||||||
|
504, 1, 0));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Destructor
|
||||||
|
virtual ~FqdnDhcpv6SrvTest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Construct the DHCPv6 Client FQDN option using flags and
|
||||||
|
/// domain-name.
|
||||||
|
///
|
||||||
|
/// @param flags Flags to be set for the created option.
|
||||||
|
/// @param fqdn_name A name which should be stored in the option.
|
||||||
|
/// @param fqdn_type A type of the name carried by the option: partial
|
||||||
|
/// or fully qualified.
|
||||||
|
///
|
||||||
|
/// @return A pointer to the created option.
|
||||||
|
Option6ClientFqdnPtr
|
||||||
|
createClientFqdn(const uint8_t flags,
|
||||||
|
const std::string& fqdn_name,
|
||||||
|
const Option6ClientFqdn::DomainNameType fqdn_type) {
|
||||||
|
return (Option6ClientFqdnPtr(new Option6ClientFqdn(flags,
|
||||||
|
fqdn_name,
|
||||||
|
fqdn_type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Create a message with or without DHCPv6 Client FQDN Option.
|
||||||
|
///
|
||||||
|
/// @param msg_type A type of the DHCPv6 message to be created.
|
||||||
|
/// @param fqdn_flags Flags to be carried in the FQDN option.
|
||||||
|
/// @param fqdn_domain_name A name to be carried in the FQDN option.
|
||||||
|
/// @param fqdn_type A type of the name carried by the option: partial
|
||||||
|
/// or fully qualified.
|
||||||
|
/// @param include_oro A boolean value which indicates whether the ORO
|
||||||
|
/// option should be added to the message (if true).
|
||||||
|
/// @param srvid server id to be stored in the message.
|
||||||
|
///
|
||||||
|
/// @return An object representing the created message.
|
||||||
|
Pkt6Ptr generateMessage(uint8_t msg_type,
|
||||||
|
const uint8_t fqdn_flags,
|
||||||
|
const std::string& fqdn_domain_name,
|
||||||
|
const Option6ClientFqdn::DomainNameType
|
||||||
|
fqdn_type,
|
||||||
|
const bool include_oro,
|
||||||
|
const bool include_fqdn = true,
|
||||||
|
OptionPtr srvid = OptionPtr()) {
|
||||||
|
Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
|
||||||
|
pkt->setRemoteAddr(IOAddress("fe80::abcd"));
|
||||||
|
Option6IAPtr ia = generateIA(D6O_IA_NA, 234, 1500, 3000);
|
||||||
|
|
||||||
|
if (msg_type != DHCPV6_REPLY) {
|
||||||
|
IOAddress hint("2001:db8:1:1::dead:beef");
|
||||||
|
OptionPtr hint_opt(new Option6IAAddr(D6O_IAADDR, hint, 300, 500));
|
||||||
|
ia->addOption(hint_opt);
|
||||||
|
pkt->addOption(ia);
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionPtr clientid = generateClientId();
|
||||||
|
pkt->addOption(clientid);
|
||||||
|
if (srvid && (msg_type != DHCPV6_SOLICIT)) {
|
||||||
|
pkt->addOption(srvid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (include_fqdn) {
|
||||||
|
pkt->addOption(createClientFqdn(fqdn_flags, fqdn_domain_name,
|
||||||
|
fqdn_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (include_oro) {
|
||||||
|
OptionUint16ArrayPtr oro(new OptionUint16Array(Option::V6,
|
||||||
|
D6O_ORO));
|
||||||
|
oro->addValue(D6O_CLIENT_FQDN);
|
||||||
|
pkt->addOption(oro);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (pkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Creates instance of the DHCPv6 message with client id and
|
||||||
|
/// server id.
|
||||||
|
///
|
||||||
|
/// @param msg_type A type of the message to be created.
|
||||||
|
/// @param srv An object representing the DHCPv6 server, which
|
||||||
|
/// is used to generate the client identifier.
|
||||||
|
///
|
||||||
|
/// @return An object representing the created message.
|
||||||
|
Pkt6Ptr generateMessageWithIds(const uint8_t msg_type,
|
||||||
|
NakedDhcpv6Srv& srv) {
|
||||||
|
Pkt6Ptr pkt = Pkt6Ptr(new Pkt6(msg_type, 1234));
|
||||||
|
// Generate client-id.
|
||||||
|
OptionPtr opt_clientid = generateClientId();
|
||||||
|
pkt->addOption(opt_clientid);
|
||||||
|
|
||||||
|
if (msg_type != DHCPV6_SOLICIT) {
|
||||||
|
// Generate server-id.
|
||||||
|
pkt->addOption(srv.getServerID());
|
||||||
|
}
|
||||||
|
|
||||||
|
return (pkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Returns an instance of the option carrying FQDN.
|
||||||
|
///
|
||||||
|
/// @param pkt A message holding FQDN option to be returned.
|
||||||
|
///
|
||||||
|
/// @return An object representing DHCPv6 Client FQDN option.
|
||||||
|
Option6ClientFqdnPtr getClientFqdnOption(const Pkt6Ptr& pkt) {
|
||||||
|
return (boost::dynamic_pointer_cast<Option6ClientFqdn>
|
||||||
|
(pkt->getOption(D6O_CLIENT_FQDN)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Adds IA option to the message.
|
||||||
|
///
|
||||||
|
/// Addded option holds an address.
|
||||||
|
///
|
||||||
|
/// @param iaid IAID
|
||||||
|
/// @param pkt A DHCPv6 message to which the IA option should be added.
|
||||||
|
void addIA(const uint32_t iaid, const IOAddress& addr, Pkt6Ptr& pkt) {
|
||||||
|
Option6IAPtr opt_ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
|
||||||
|
Option6IAAddrPtr opt_iaaddr(new Option6IAAddr(D6O_IAADDR, addr,
|
||||||
|
300, 500));
|
||||||
|
opt_ia->addOption(opt_iaaddr);
|
||||||
|
pkt->addOption(opt_ia);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Adds IA option to the message.
|
||||||
|
///
|
||||||
|
/// Added option holds status code.
|
||||||
|
///
|
||||||
|
/// @param iaid IAID
|
||||||
|
/// @param status_code Status code
|
||||||
|
/// @param pkt A DHCPv6 message to which the option should be added.
|
||||||
|
void addIA(const uint32_t iaid, const uint16_t status_code, Pkt6Ptr& pkt) {
|
||||||
|
Option6IAPtr opt_ia = generateIA(D6O_IA_NA, iaid, 1500, 3000);
|
||||||
|
addStatusCode(status_code, "", opt_ia);
|
||||||
|
pkt->addOption(opt_ia);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Creates status code with the specified code and message.
|
||||||
|
///
|
||||||
|
/// @param code A status code.
|
||||||
|
/// @param msg A string representation of the message to be added to the
|
||||||
|
/// Status Code option.
|
||||||
|
///
|
||||||
|
/// @return An object representing the Status Code option.
|
||||||
|
OptionCustomPtr createStatusCode(const uint16_t code,
|
||||||
|
const std::string& msg) {
|
||||||
|
OptionDefinition def("status-code", D6O_STATUS_CODE, "record");
|
||||||
|
def.addRecordField("uint16");
|
||||||
|
def.addRecordField("string");
|
||||||
|
OptionCustomPtr opt_status(new OptionCustom(def, Option::V6));
|
||||||
|
opt_status->writeInteger(code);
|
||||||
|
if (!msg.empty()) {
|
||||||
|
opt_status->writeString(msg, 1);
|
||||||
|
}
|
||||||
|
return (opt_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Adds Status Code option to the IA.
|
||||||
|
///
|
||||||
|
/// @param code A status code value.
|
||||||
|
/// @param msg A string representation of the message to be added to the
|
||||||
|
/// Status Code option.
|
||||||
|
void addStatusCode(const uint16_t code, const std::string& msg,
|
||||||
|
Option6IAPtr& opt_ia) {
|
||||||
|
opt_ia->addOption(createStatusCode(code, msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Verifies if the DHCPv6 server processes DHCPv6 Client FQDN option
|
||||||
|
/// as expected.
|
||||||
|
///
|
||||||
|
/// This function simulates generation of the client's message holding FQDN.
|
||||||
|
/// It then calls the server's @c Dhcpv6Srv::processClientFqdn option to
|
||||||
|
/// generate server's response to the FQDN. This function returns the FQDN
|
||||||
|
/// which should be appended to the server's response to the client.
|
||||||
|
/// This function verifies that the FQDN option returned is correct.
|
||||||
|
///
|
||||||
|
/// @param msg_type A type of the client's message.
|
||||||
|
/// @param use_oro A boolean value which indicates whether the DHCPv6 ORO
|
||||||
|
/// option (requesting return of the FQDN option by the server) should be
|
||||||
|
/// included in the client's message (if true), or not included (if false).
|
||||||
|
/// @param in_flags A value of flags field to be set for the FQDN carried
|
||||||
|
/// in the client's message.
|
||||||
|
/// @param in_domain_name A domain name to be carried in the client's FQDN
|
||||||
|
/// option.
|
||||||
|
/// @param in_domain_type A type of the domain name to be carried in the
|
||||||
|
/// client's FQDM option (partial or fully qualified).
|
||||||
|
/// @param exp_flags A value of flags expected in the FQDN sent by a server.
|
||||||
|
/// @param exp_domain_name A domain name expected in the FQDN sent by a
|
||||||
|
/// server.
|
||||||
|
void testFqdn(const uint16_t msg_type,
|
||||||
|
const bool use_oro,
|
||||||
|
const uint8_t in_flags,
|
||||||
|
const std::string& in_domain_name,
|
||||||
|
const Option6ClientFqdn::DomainNameType in_domain_type,
|
||||||
|
const uint8_t exp_flags,
|
||||||
|
const std::string& exp_domain_name) {
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
Pkt6Ptr question = generateMessage(msg_type,
|
||||||
|
in_flags,
|
||||||
|
in_domain_name,
|
||||||
|
in_domain_type,
|
||||||
|
use_oro);
|
||||||
|
ASSERT_TRUE(getClientFqdnOption(question));
|
||||||
|
|
||||||
|
Option6ClientFqdnPtr answ_fqdn;
|
||||||
|
ASSERT_NO_THROW(answ_fqdn = srv.processClientFqdn(question));
|
||||||
|
ASSERT_TRUE(answ_fqdn);
|
||||||
|
|
||||||
|
const bool flag_n = (exp_flags & Option6ClientFqdn::FLAG_N) != 0;
|
||||||
|
const bool flag_s = (exp_flags & Option6ClientFqdn::FLAG_S) != 0;
|
||||||
|
const bool flag_o = (exp_flags & Option6ClientFqdn::FLAG_O) != 0;
|
||||||
|
|
||||||
|
EXPECT_EQ(flag_n, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_N));
|
||||||
|
EXPECT_EQ(flag_s, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_S));
|
||||||
|
EXPECT_EQ(flag_o, answ_fqdn->getFlag(Option6ClientFqdn::FLAG_O));
|
||||||
|
|
||||||
|
EXPECT_EQ(exp_domain_name, answ_fqdn->getDomainName());
|
||||||
|
EXPECT_EQ(Option6ClientFqdn::FULL, answ_fqdn->getDomainNameType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Tests that the client's message holding an FQDN is processed
|
||||||
|
/// and that lease is acquired.
|
||||||
|
///
|
||||||
|
/// @param msg_type A type of the client's message.
|
||||||
|
/// @param hostname A domain name in the client's FQDN.
|
||||||
|
/// @param srv A server object, used to process the message.
|
||||||
|
/// @param test_fqdn A boolean value which indicates whether the FQDN
|
||||||
|
/// option should be included in the client's message (if true) or not
|
||||||
|
/// (if false). In the former case, the function will expect that server
|
||||||
|
/// responds with the FQDN option. In the latter case, the function expects
|
||||||
|
/// that the server doesn't respond with the FQDN.
|
||||||
|
void testProcessMessage(const uint8_t msg_type,
|
||||||
|
const std::string& hostname,
|
||||||
|
NakedDhcpv6Srv& srv,
|
||||||
|
const bool test_fqdn = true) {
|
||||||
|
// Create a message of a specified type, add server id and
|
||||||
|
// FQDN option.
|
||||||
|
OptionPtr srvid = srv.getServerID();
|
||||||
|
Pkt6Ptr req = generateMessage(msg_type, Option6ClientFqdn::FLAG_S,
|
||||||
|
hostname,
|
||||||
|
Option6ClientFqdn::FULL,
|
||||||
|
true, test_fqdn, srvid);
|
||||||
|
|
||||||
|
// For different client's message types we have to invoke different
|
||||||
|
// functions to generate response.
|
||||||
|
Pkt6Ptr reply;
|
||||||
|
if (msg_type == DHCPV6_SOLICIT) {
|
||||||
|
ASSERT_NO_THROW(reply = srv.processSolicit(req));
|
||||||
|
|
||||||
|
} else if (msg_type == DHCPV6_REQUEST) {
|
||||||
|
ASSERT_NO_THROW(reply = srv.processRequest(req));
|
||||||
|
|
||||||
|
} else if (msg_type == DHCPV6_RENEW) {
|
||||||
|
ASSERT_NO_THROW(reply = srv.processRequest(req));
|
||||||
|
|
||||||
|
} else if (msg_type == DHCPV6_RELEASE) {
|
||||||
|
// For Release no lease will be acquired so we have to leave
|
||||||
|
// function here.
|
||||||
|
ASSERT_NO_THROW(reply = srv.processRelease(req));
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// We are not interested in testing other message types.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For Solicit, we will get different message type obviously.
|
||||||
|
if (msg_type == DHCPV6_SOLICIT) {
|
||||||
|
checkResponse(reply, DHCPV6_ADVERTISE, 1234);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
checkResponse(reply, DHCPV6_REPLY, 1234);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check verify that IA_NA is correct.
|
||||||
|
Option6IAAddrPtr addr =
|
||||||
|
checkIA_NA(reply, 234, subnet_->getT1(), subnet_->getT2());
|
||||||
|
ASSERT_TRUE(addr);
|
||||||
|
|
||||||
|
// Check that we have got the address we requested.
|
||||||
|
checkIAAddr(addr, IOAddress("2001:db8:1:1::dead:beef"),
|
||||||
|
Lease::TYPE_NA);
|
||||||
|
|
||||||
|
if (msg_type != DHCPV6_SOLICIT) {
|
||||||
|
// Check that the lease exists.
|
||||||
|
Lease6Ptr lease =
|
||||||
|
checkLease(duid_, reply->getOption(D6O_IA_NA), addr);
|
||||||
|
ASSERT_TRUE(lease);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test_fqdn) {
|
||||||
|
ASSERT_TRUE(reply->getOption(D6O_CLIENT_FQDN));
|
||||||
|
} else {
|
||||||
|
ASSERT_FALSE(reply->getOption(D6O_CLIENT_FQDN));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Verify that NameChangeRequest holds valid values.
|
||||||
|
///
|
||||||
|
/// This function picks first NameChangeRequest from the internal server's
|
||||||
|
/// queue and checks that it holds valid parameters. The NameChangeRequest
|
||||||
|
/// is removed from the queue.
|
||||||
|
///
|
||||||
|
/// @param srv A server object holding a queue of NameChangeRequests.
|
||||||
|
/// @param type An expected type of the NameChangeRequest (Add or Remove).
|
||||||
|
/// @param reverse An expected setting of the reverse update flag.
|
||||||
|
/// @param forward An expected setting of the forward udpate flag.
|
||||||
|
/// @param addr A string representation of the IPv6 address held in the
|
||||||
|
/// NameChangeRequest.
|
||||||
|
/// @param dhcid An expected DHCID value.
|
||||||
|
/// @param expires A timestamp when the lease associated with the
|
||||||
|
/// NameChangeRequest expires.
|
||||||
|
/// @param len A valid lifetime of the lease associated with the
|
||||||
|
/// NameChangeRequest.
|
||||||
|
void verifyNameChangeRequest(NakedDhcpv6Srv& srv,
|
||||||
|
const isc::dhcp_ddns::NameChangeType type,
|
||||||
|
const bool reverse, const bool forward,
|
||||||
|
const std::string& addr,
|
||||||
|
const std::string& dhcid,
|
||||||
|
const uint16_t expires,
|
||||||
|
const uint16_t len) {
|
||||||
|
NameChangeRequest ncr = srv.name_change_reqs_.front();
|
||||||
|
EXPECT_EQ(type, ncr.getChangeType());
|
||||||
|
EXPECT_EQ(forward, ncr.isForwardChange());
|
||||||
|
EXPECT_EQ(reverse, ncr.isReverseChange());
|
||||||
|
EXPECT_EQ(addr, ncr.getIpAddress());
|
||||||
|
EXPECT_EQ(dhcid, ncr.getDhcid().toStr());
|
||||||
|
EXPECT_EQ(expires, ncr.getLeaseExpiresOn());
|
||||||
|
EXPECT_EQ(len, ncr.getLeaseLength());
|
||||||
|
EXPECT_EQ(isc::dhcp_ddns::ST_NEW, ncr.getStatus());
|
||||||
|
srv.name_change_reqs_.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Holds a lease used by a test.
|
||||||
|
Lease6Ptr lease_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// A set of tests verifying server's behaviour when it receives the DHCPv6
|
||||||
|
// Client Fqdn Option.
|
||||||
|
// @todo: Extend these tests once appropriate configuration parameters are
|
||||||
|
// implemented (ticket #3034).
|
||||||
|
|
||||||
|
// Test server's response when client requests that server performs AAAA update.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdate) {
|
||||||
|
testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S,
|
||||||
|
"myhost.example.com",
|
||||||
|
Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_S,
|
||||||
|
"myhost.example.com.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test server's response when client provides partial domain-name and requests
|
||||||
|
// that server performs AAAA update.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdatePartialName) {
|
||||||
|
testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S, "myhost",
|
||||||
|
Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
|
||||||
|
"myhost.example.com.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test server's response when client provides empty domain-name and requests
|
||||||
|
// that server performs AAAA update.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, serverAAAAUpdateNoName) {
|
||||||
|
testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_S, "",
|
||||||
|
Option6ClientFqdn::PARTIAL, Option6ClientFqdn::FLAG_S,
|
||||||
|
"myhost.example.com.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test server's response when client requests no DNS update.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, noUpdate) {
|
||||||
|
testFqdn(DHCPV6_SOLICIT, true, Option6ClientFqdn::FLAG_N,
|
||||||
|
"myhost.example.com",
|
||||||
|
Option6ClientFqdn::FULL, Option6ClientFqdn::FLAG_N,
|
||||||
|
"myhost.example.com.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test server's response when client requests that server delegates the AAAA
|
||||||
|
// update to the client and this delegation is not allowed.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, clientAAAAUpdateNotAllowed) {
|
||||||
|
testFqdn(DHCPV6_SOLICIT, true, 0, "myhost.example.com.",
|
||||||
|
Option6ClientFqdn::FULL,
|
||||||
|
Option6ClientFqdn::FLAG_S | Option6ClientFqdn::FLAG_O,
|
||||||
|
"myhost.example.com.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that exception is thrown if supplied NULL answer packet when
|
||||||
|
// creating NameChangeRequests.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAnswer) {
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
|
Pkt6Ptr answer;
|
||||||
|
Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
|
||||||
|
"myhost.example.com",
|
||||||
|
Option6ClientFqdn::FULL);
|
||||||
|
EXPECT_THROW(srv.createNameChangeRequests(answer, fqdn),
|
||||||
|
isc::Unexpected);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that exception is thrown if supplied answer from the server
|
||||||
|
// contains no DUID when creating NameChangeRequests.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoDUID) {
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
|
Pkt6Ptr answer = Pkt6Ptr(new Pkt6(DHCPV6_REPLY, 1234));
|
||||||
|
Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
|
||||||
|
"myhost.example.com",
|
||||||
|
Option6ClientFqdn::FULL);
|
||||||
|
|
||||||
|
EXPECT_THROW(srv.createNameChangeRequests(answer, fqdn),
|
||||||
|
isc::Unexpected);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test no NameChangeRequests are added if FQDN option is NULL.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoFQDN) {
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
|
// Create Reply message with Client Id and Server id.
|
||||||
|
Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
|
||||||
|
|
||||||
|
// Pass NULL FQDN option. No NameChangeRequests should be created.
|
||||||
|
Option6ClientFqdnPtr fqdn;
|
||||||
|
ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
|
||||||
|
|
||||||
|
// There should be no new NameChangeRequests.
|
||||||
|
EXPECT_TRUE(srv.name_change_reqs_.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that NameChangeRequests are not generated if an answer message
|
||||||
|
// contains no addresses.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequestsNoAddr) {
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
|
// Create Reply message with Client Id and Server id.
|
||||||
|
Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
|
||||||
|
|
||||||
|
Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
|
||||||
|
"myhost.example.com",
|
||||||
|
Option6ClientFqdn::FULL);
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
|
||||||
|
|
||||||
|
// We didn't add any IAs, so there should be no NameChangeRequests in th
|
||||||
|
// queue.
|
||||||
|
ASSERT_TRUE(srv.name_change_reqs_.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that a number of NameChangeRequests is created as a result of
|
||||||
|
// processing the answer message which holds 3 IAs and when FQDN is
|
||||||
|
// specified.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, createNameChangeRequests) {
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
|
// Create Reply message with Client Id and Server id.
|
||||||
|
Pkt6Ptr answer = generateMessageWithIds(DHCPV6_REPLY, srv);
|
||||||
|
|
||||||
|
// Create three IAs, each having different address.
|
||||||
|
addIA(1234, IOAddress("2001:db8:1::1"), answer);
|
||||||
|
addIA(2345, IOAddress("2001:db8:1::2"), answer);
|
||||||
|
addIA(3456, IOAddress("2001:db8:1::3"), answer);
|
||||||
|
|
||||||
|
// Use domain name in upper case. It should be converted to lower-case
|
||||||
|
// before DHCID is calculated. So, we should get the same result as if
|
||||||
|
// we typed domain name in lower-case.
|
||||||
|
Option6ClientFqdnPtr fqdn = createClientFqdn(Option6ClientFqdn::FLAG_S,
|
||||||
|
"MYHOST.EXAMPLE.COM",
|
||||||
|
Option6ClientFqdn::FULL);
|
||||||
|
|
||||||
|
// Create NameChangeRequests. Since we have added 3 IAs, it should
|
||||||
|
// result in generation of 3 distinct NameChangeRequests.
|
||||||
|
ASSERT_NO_THROW(srv.createNameChangeRequests(answer, fqdn));
|
||||||
|
ASSERT_EQ(3, srv.name_change_reqs_.size());
|
||||||
|
|
||||||
|
// Verify that NameChangeRequests are correct. Each call to the
|
||||||
|
// verifyNameChangeRequest will pop verified request from the queue.
|
||||||
|
|
||||||
|
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
|
||||||
|
"2001:db8:1::1",
|
||||||
|
"000201415AA33D1187D148275136FA30300478"
|
||||||
|
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
||||||
|
0, 500);
|
||||||
|
|
||||||
|
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
|
||||||
|
"2001:db8:1::2",
|
||||||
|
"000201415AA33D1187D148275136FA30300478"
|
||||||
|
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
||||||
|
0, 500);
|
||||||
|
|
||||||
|
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
|
||||||
|
"2001:db8:1::3",
|
||||||
|
"000201415AA33D1187D148275136FA30300478"
|
||||||
|
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
||||||
|
0, 500);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test creation of the NameChangeRequest to remove both forward and reverse
|
||||||
|
// mapping for the given lease.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestFwdRev) {
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
|
lease_->fqdn_fwd_ = true;
|
||||||
|
lease_->fqdn_rev_ = true;
|
||||||
|
// Part of the domain name is in upper case, to test that it gets converted
|
||||||
|
// to lower case before DHCID is computed. So, we should get the same DHCID
|
||||||
|
// as if we typed domain-name in lower case.
|
||||||
|
lease_->hostname_ = "MYHOST.example.com.";
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
|
||||||
|
|
||||||
|
ASSERT_EQ(1, srv.name_change_reqs_.size());
|
||||||
|
|
||||||
|
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
|
||||||
|
"2001:db8:1::1",
|
||||||
|
"000201415AA33D1187D148275136FA30300478"
|
||||||
|
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
||||||
|
0, 502);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test creation of the NameChangeRequest to remove reverse mapping for the
|
||||||
|
// given lease.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestRev) {
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
|
lease_->fqdn_fwd_ = false;
|
||||||
|
lease_->fqdn_rev_ = true;
|
||||||
|
lease_->hostname_ = "myhost.example.com.";
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
|
||||||
|
|
||||||
|
ASSERT_EQ(1, srv.name_change_reqs_.size());
|
||||||
|
|
||||||
|
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, false,
|
||||||
|
"2001:db8:1::1",
|
||||||
|
"000201415AA33D1187D148275136FA30300478"
|
||||||
|
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
||||||
|
0, 502);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that NameChangeRequest to remove DNS records is not generated when
|
||||||
|
// neither forward nor reverse DNS update has been performed for a lease.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoUpdate) {
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
|
lease_->fqdn_fwd_ = false;
|
||||||
|
lease_->fqdn_rev_ = false;
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
|
||||||
|
|
||||||
|
EXPECT_TRUE(srv.name_change_reqs_.empty());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that NameChangeRequest is not generated if the hostname hasn't been
|
||||||
|
// specified for a lease for which forward and reverse mapping has been set.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestNoHostname) {
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
|
lease_->fqdn_fwd_ = true;
|
||||||
|
lease_->fqdn_rev_ = true;
|
||||||
|
lease_->hostname_ = "";
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
|
||||||
|
|
||||||
|
EXPECT_TRUE(srv.name_change_reqs_.empty());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that NameChangeRequest is not generated if the invalid hostname has
|
||||||
|
// been specified for a lease for which forward and reverse mapping has been
|
||||||
|
// set.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, createRemovalNameChangeRequestWrongHostname) {
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
|
lease_->fqdn_fwd_ = true;
|
||||||
|
lease_->fqdn_rev_ = true;
|
||||||
|
lease_->hostname_ = "myhost..example.com.";
|
||||||
|
|
||||||
|
ASSERT_NO_THROW(srv.createRemovalNameChangeRequest(lease_));
|
||||||
|
|
||||||
|
EXPECT_TRUE(srv.name_change_reqs_.empty());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that Advertise message generated in a response to the Solicit will
|
||||||
|
// not result in generation if the NameChangeRequests.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, processSolicit) {
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
|
// Create a Solicit message with FQDN option and generate server's
|
||||||
|
// response using processSolicit function.
|
||||||
|
testProcessMessage(DHCPV6_SOLICIT, "myhost.example.com", srv);
|
||||||
|
EXPECT_TRUE(srv.name_change_reqs_.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that client may send two requests, each carrying FQDN option with
|
||||||
|
// a different domain-name. Server should use existing lease for the second
|
||||||
|
// request but modify the DNS entries for the lease according to the contents
|
||||||
|
// of the FQDN sent in the second request.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, processTwoRequests) {
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
|
// Create a Request message with FQDN option and generate server's
|
||||||
|
// response using processRequest function. This will result in the
|
||||||
|
// creation of a new lease and the appropriate NameChangeRequest
|
||||||
|
// to add both reverse and forward mapping to DNS.
|
||||||
|
testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
|
||||||
|
ASSERT_EQ(1, srv.name_change_reqs_.size());
|
||||||
|
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
|
||||||
|
"2001:db8:1:1::dead:beef",
|
||||||
|
"000201415AA33D1187D148275136FA30300478"
|
||||||
|
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
||||||
|
0, 4000);
|
||||||
|
|
||||||
|
// Client may send another request message with a new domain-name. In this
|
||||||
|
// case the same lease will be returned. The existing DNS entry needs to
|
||||||
|
// be replaced with a new one. Server should determine that the different
|
||||||
|
// FQDN has been already added to the DNS. As a result, the old DNS
|
||||||
|
// entries should be removed and the entries for the new domain-name
|
||||||
|
// should be added. Therefore, we expect two NameChangeRequests. One to
|
||||||
|
// remove the existing entries, one to add new entries.
|
||||||
|
testProcessMessage(DHCPV6_REQUEST, "otherhost.example.com", srv);
|
||||||
|
ASSERT_EQ(2, srv.name_change_reqs_.size());
|
||||||
|
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
|
||||||
|
"2001:db8:1:1::dead:beef",
|
||||||
|
"000201415AA33D1187D148275136FA30300478"
|
||||||
|
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
||||||
|
0, 4000);
|
||||||
|
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
|
||||||
|
"2001:db8:1:1::dead:beef",
|
||||||
|
"000201D422AA463306223D269B6CB7AFE7AAD265FC"
|
||||||
|
"EA97F93623019B2E0D14E5323D5A",
|
||||||
|
0, 4000);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that client may send Request followed by the Renew, both holding
|
||||||
|
// FQDN options, but each option holding different domain-name. The Renew
|
||||||
|
// should result in generation of the two NameChangeRequests, one to remove
|
||||||
|
// DNS entry added previously when Request was processed, another one to
|
||||||
|
// add a new entry for the FQDN held in the Renew.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, processRequestRenew) {
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
|
// Create a Request message with FQDN option and generate server's
|
||||||
|
// response using processRequest function. This will result in the
|
||||||
|
// creation of a new lease and the appropriate NameChangeRequest
|
||||||
|
// to add both reverse and forward mapping to DNS.
|
||||||
|
testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
|
||||||
|
ASSERT_EQ(1, srv.name_change_reqs_.size());
|
||||||
|
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
|
||||||
|
"2001:db8:1:1::dead:beef",
|
||||||
|
"000201415AA33D1187D148275136FA30300478"
|
||||||
|
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
||||||
|
0, 4000);
|
||||||
|
|
||||||
|
// Client may send Renew message with a new domain-name. In this
|
||||||
|
// case the same lease will be returned. The existing DNS entry needs to
|
||||||
|
// be replaced with a new one. Server should determine that the different
|
||||||
|
// FQDN has been already added to the DNS. As a result, the old DNS
|
||||||
|
// entries should be removed and the entries for the new domain-name
|
||||||
|
// should be added. Therefore, we expect two NameChangeRequests. One to
|
||||||
|
// remove the existing entries, one to add new entries.
|
||||||
|
testProcessMessage(DHCPV6_RENEW, "otherhost.example.com", srv);
|
||||||
|
ASSERT_EQ(2, srv.name_change_reqs_.size());
|
||||||
|
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
|
||||||
|
"2001:db8:1:1::dead:beef",
|
||||||
|
"000201415AA33D1187D148275136FA30300478"
|
||||||
|
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
||||||
|
0, 4000);
|
||||||
|
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
|
||||||
|
"2001:db8:1:1::dead:beef",
|
||||||
|
"000201D422AA463306223D269B6CB7AFE7AAD265FC"
|
||||||
|
"EA97F93623019B2E0D14E5323D5A",
|
||||||
|
0, 4000);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, processRequestRelease) {
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
|
// Create a Request message with FQDN option and generate server's
|
||||||
|
// response using processRequest function. This will result in the
|
||||||
|
// creation of a new lease and the appropriate NameChangeRequest
|
||||||
|
// to add both reverse and forward mapping to DNS.
|
||||||
|
testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv);
|
||||||
|
ASSERT_EQ(1, srv.name_change_reqs_.size());
|
||||||
|
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_ADD, true, true,
|
||||||
|
"2001:db8:1:1::dead:beef",
|
||||||
|
"000201415AA33D1187D148275136FA30300478"
|
||||||
|
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
||||||
|
0, 4000);
|
||||||
|
|
||||||
|
// Client may send Release message. In this case the lease should be
|
||||||
|
// removed and all existing DNS entries for this lease should be
|
||||||
|
// also removed. Therefore, we expect that single NameChangeRequest to
|
||||||
|
// remove DNS entries is generated.
|
||||||
|
testProcessMessage(DHCPV6_RELEASE, "otherhost.example.com", srv);
|
||||||
|
ASSERT_EQ(1, srv.name_change_reqs_.size());
|
||||||
|
verifyNameChangeRequest(srv, isc::dhcp_ddns::CHG_REMOVE, true, true,
|
||||||
|
"2001:db8:1:1::dead:beef",
|
||||||
|
"000201415AA33D1187D148275136FA30300478"
|
||||||
|
"FAAAA3EBD29826B5C907B2C9268A6F52",
|
||||||
|
0, 4000);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks that the server does not include DHCPv6 Client FQDN option in its
|
||||||
|
// response when client doesn't include this option in a Request.
|
||||||
|
TEST_F(FqdnDhcpv6SrvTest, processRequestWithoutFqdn) {
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
|
// The last parameter disables the use of DHCPv6 Client FQDN option
|
||||||
|
// in the client's Request. In this case, we expect that the FQDN
|
||||||
|
// option will not be included in the server's response. The
|
||||||
|
// testProcessMessage will check that.
|
||||||
|
testProcessMessage(DHCPV6_REQUEST, "myhost.example.com", srv, false);
|
||||||
|
ASSERT_TRUE(srv.name_change_reqs_.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end of anonymous namespace
|
Reference in New Issue
Block a user