mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 21:45:37 +00:00
[master] Merge branch 'trac2597' (server-id is now stored by dhcpv{4,6})
Conflicts: ChangeLog src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
This commit is contained in:
@@ -1,3 +1,11 @@
|
|||||||
|
5XX. [func] tomek
|
||||||
|
b10-dhcp4: The DHCPv4 server now generates a server identifier
|
||||||
|
the first time it is run. The identifier is preserved in a file
|
||||||
|
across server restarts.
|
||||||
|
b10-dhcp6: The server identifier is now preserved in a file across
|
||||||
|
server restarts.
|
||||||
|
(Trac #2597, git fa342a994de5dbefe32996be7eebe58f6304cff7)
|
||||||
|
|
||||||
549. [func] tomek
|
549. [func] tomek
|
||||||
b10-dhcp6: It is now possible to specify that a configured subnet
|
b10-dhcp6: It is now possible to specify that a configured subnet
|
||||||
is reachable locally over specified interface (see "interface"
|
is reachable locally over specified interface (see "interface"
|
||||||
|
@@ -3485,18 +3485,34 @@ Dhcp4/subnet4 [] list (default)</screen>
|
|||||||
src/bin/dhcp6/dhcp4_srv.cc file, modify the following parameters and
|
src/bin/dhcp6/dhcp4_srv.cc file, modify the following parameters and
|
||||||
recompile:
|
recompile:
|
||||||
<screen>
|
<screen>
|
||||||
const std::string HARDCODED_LEASE = "192.0.2.222"; // assigned lease
|
|
||||||
const std::string HARDCODED_NETMASK = "255.255.255.0";
|
|
||||||
const uint32_t HARDCODED_LEASE_TIME = 60; // in seconds
|
|
||||||
const std::string HARDCODED_GATEWAY = "192.0.2.1";
|
const std::string HARDCODED_GATEWAY = "192.0.2.1";
|
||||||
const std::string HARDCODED_DNS_SERVER = "192.0.2.2";
|
const std::string HARDCODED_DNS_SERVER = "192.0.2.2";
|
||||||
const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";
|
const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";</screen>
|
||||||
const std::string HARDCODED_SERVER_ID = "192.0.2.1";</screen>
|
|
||||||
|
|
||||||
Lease database and configuration support is planned for end of 2012.
|
Lease database and configuration support is planned for end of 2012.
|
||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section id="dhcp4-serverid">
|
||||||
|
<title>Server Identifier in DHCPv4</title>
|
||||||
|
<para>The DHCPv4 protocol uses a "server identifier" for clients to be able
|
||||||
|
to discriminate between several servers present on the same link: this
|
||||||
|
value is an IPv4 address of the server. When started for the first time,
|
||||||
|
the DHCPv4 server will choose one of its IPv4 addresses as its server-id,
|
||||||
|
and store the chosen value to a file. (The file is named b10-dhcp4-serverid and is
|
||||||
|
stored in the "local state directory". This is set during installation
|
||||||
|
when "configure" is run, and can be changed by using "--localstatedir"
|
||||||
|
on the "configure" command line.) That file will be read by the server
|
||||||
|
and the contained value used whenever the server is subsequently started.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
It is unlikely that this parameter needs to be changed. If such a need
|
||||||
|
arises, please stop the server, edit the file and restart the server.
|
||||||
|
It is a text file that should contain an IPv4 address. Spaces are
|
||||||
|
ignored. No extra characters are allowed in this file.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section id="dhcp4-std">
|
<section id="dhcp4-std">
|
||||||
<title>Supported standards</title>
|
<title>Supported standards</title>
|
||||||
<para>The following standards and draft standards are currently
|
<para>The following standards and draft standards are currently
|
||||||
@@ -3841,6 +3857,31 @@ Dhcp6/subnet6 [] list (default)</screen>
|
|||||||
</note>
|
</note>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section id="dhcp6-serverid">
|
||||||
|
<title>Server Identifier in DHCPv6</title>
|
||||||
|
<para>The DHCPv6 protocol uses a "server identifier" (also known
|
||||||
|
as a DUID) for clients to be able to discriminate between several
|
||||||
|
servers present on the same link. There are several types of
|
||||||
|
DUIDs defined, but RFC 3315 instructs servers to use DUID-LLT if
|
||||||
|
possible. This format consists of a link-layer (MAC) address and a
|
||||||
|
timestamp. When started for the first time, the DHCPv6 server will
|
||||||
|
automatically generate such a DUID and store the chosen value to
|
||||||
|
a file (The file is named b10-dhcp6-serverid and is stored in the
|
||||||
|
"local state directory". This is set during installation when
|
||||||
|
"configure" is run, and can be changed by using "--localstatedir"
|
||||||
|
on the "configure" command line.) That file will be read by the server
|
||||||
|
and the contained value used whenever the server is subsequently started.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
It is unlikely that this parameter needs to be changed. If such a need
|
||||||
|
arises, please stop the server, edit the file and start the server
|
||||||
|
again. It is a text file that contains double digit hexadecimal values
|
||||||
|
separated by colons. This format is similar to typical MAC address
|
||||||
|
format. Spaces are ignored. No extra characters are allowed in this
|
||||||
|
file.
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section id="dhcp6-std">
|
<section id="dhcp6-std">
|
||||||
<title>Supported DHCPv6 Standards</title>
|
<title>Supported DHCPv6 Standards</title>
|
||||||
<para>The following standards and draft standards are currently
|
<para>The following standards and draft standards are currently
|
||||||
|
@@ -162,6 +162,26 @@ A debug message listing the data returned to the client.
|
|||||||
The IPv4 DHCP server has encountered a fatal error and is terminating.
|
The IPv4 DHCP server has encountered a fatal error and is terminating.
|
||||||
The reason for the failure is included in the message.
|
The reason for the failure is included in the message.
|
||||||
|
|
||||||
|
% DHCP4_SERVERID_GENERATED server-id %1 has been generated and will be stored in %2
|
||||||
|
This informational messages indicates that the server was not able to
|
||||||
|
read its server identifier and has generated a new one. This server-id
|
||||||
|
will be stored in a file and will be read (and used) whenever the server
|
||||||
|
is restarted. This is normal behavior when the server is started for the
|
||||||
|
first time. If this message is printed every time the server is started,
|
||||||
|
please check that the server has sufficient permission to write its
|
||||||
|
server-id file and that the file is not corrupt.
|
||||||
|
|
||||||
|
% DHCP4_SERVERID_LOADED server-id %1 has been loaded from file %2
|
||||||
|
This debug message indicates that the server loaded its server identifier.
|
||||||
|
That value is sent in all server responses and clients use it to
|
||||||
|
discriminate between servers. This is a part of normal startup or
|
||||||
|
reconfiguration procedure.
|
||||||
|
|
||||||
|
% DHCP4_SERVERID_WRITE_FAIL server was not able to write its ID to file %1
|
||||||
|
This warning message indicates that server was not able to write its
|
||||||
|
server identifier to a file. The most likely cause is is that the server
|
||||||
|
does not have permissions to write the server id file.
|
||||||
|
|
||||||
% DHCP4_SESSION_FAIL failed to establish BIND 10 session (%1), running stand-alone
|
% DHCP4_SESSION_FAIL failed to establish BIND 10 session (%1), running stand-alone
|
||||||
The server has failed to establish communication with the rest of BIND
|
The server has failed to establish communication with the rest of BIND
|
||||||
10 and is running in stand-alone mode. (This behavior will change once
|
10 and is running in stand-alone mode. (This behavior will change once
|
||||||
|
@@ -30,6 +30,11 @@
|
|||||||
#include <dhcpsrv/utils.h>
|
#include <dhcpsrv/utils.h>
|
||||||
#include <dhcpsrv/addr_utilities.h>
|
#include <dhcpsrv/addr_utilities.h>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/erase.hpp>
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
using namespace isc;
|
using namespace isc;
|
||||||
using namespace isc::asiolink;
|
using namespace isc::asiolink;
|
||||||
using namespace isc::dhcp;
|
using namespace isc::dhcp;
|
||||||
@@ -41,7 +46,6 @@ using namespace std;
|
|||||||
const std::string HARDCODED_GATEWAY = "192.0.2.1";
|
const std::string HARDCODED_GATEWAY = "192.0.2.1";
|
||||||
const std::string HARDCODED_DNS_SERVER = "192.0.2.2";
|
const std::string HARDCODED_DNS_SERVER = "192.0.2.2";
|
||||||
const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";
|
const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";
|
||||||
const std::string HARDCODED_SERVER_ID = "192.0.2.1";
|
|
||||||
|
|
||||||
Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig) {
|
Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig) {
|
||||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
|
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_OPEN_SOCKET).arg(port);
|
||||||
@@ -56,7 +60,22 @@ Dhcpv4Srv::Dhcpv4Srv(uint16_t port, const char* dbconfig) {
|
|||||||
IfaceMgr::instance().openSockets4(port);
|
IfaceMgr::instance().openSockets4(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
setServerID();
|
string srvid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_ID_FILE);
|
||||||
|
if (loadServerID(srvid_file)) {
|
||||||
|
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_START, DHCP4_SERVERID_LOADED)
|
||||||
|
.arg(srvid_file);
|
||||||
|
} else {
|
||||||
|
generateServerID();
|
||||||
|
LOG_INFO(dhcp4_logger, DHCP4_SERVERID_GENERATED)
|
||||||
|
.arg(srvidToString(getServerID()))
|
||||||
|
.arg(srvid_file);
|
||||||
|
|
||||||
|
if (!writeServerID(srvid_file)) {
|
||||||
|
LOG_WARN(dhcp4_logger, DHCP4_SERVERID_WRITE_FAIL)
|
||||||
|
.arg(srvid_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Instantiate LeaseMgr
|
// Instantiate LeaseMgr
|
||||||
LeaseMgrFactory::create(dbconfig);
|
LeaseMgrFactory::create(dbconfig);
|
||||||
@@ -176,19 +195,108 @@ Dhcpv4Srv::run() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO add support for config session (see src/bin/auth/main.cc)
|
|
||||||
// so this daemon can be controlled from bob
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (true);
|
return (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool Dhcpv4Srv::loadServerID(const std::string& file_name) {
|
||||||
Dhcpv4Srv::setServerID() {
|
|
||||||
/// @todo: implement this for real (see ticket #2588)
|
// load content of the file into a string
|
||||||
serverid_ = OptionPtr(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
|
fstream f(file_name.c_str(), ios::in);
|
||||||
IOAddress(HARDCODED_SERVER_ID)));
|
if (!f.is_open()) {
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
string hex_string;
|
||||||
|
f >> hex_string;
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
// remove any spaces
|
||||||
|
boost::algorithm::erase_all(hex_string, " ");
|
||||||
|
|
||||||
|
try {
|
||||||
|
IOAddress addr(hex_string);
|
||||||
|
|
||||||
|
if (!addr.isV4()) {
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now create server-id option
|
||||||
|
serverid_.reset(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER, addr));
|
||||||
|
|
||||||
|
} catch(...) {
|
||||||
|
// any kind of malformed input (empty string, IPv6 address, complete
|
||||||
|
// garbate etc.)
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dhcpv4Srv::generateServerID() {
|
||||||
|
|
||||||
|
const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
|
||||||
|
|
||||||
|
// Let's find suitable interface.
|
||||||
|
for (IfaceMgr::IfaceCollection::const_iterator iface = ifaces.begin();
|
||||||
|
iface != ifaces.end(); ++iface) {
|
||||||
|
|
||||||
|
// Let's don't use loopback.
|
||||||
|
if (iface->flag_loopback_) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's skip downed interfaces. It is better to use working ones.
|
||||||
|
if (!iface->flag_up_) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IfaceMgr::AddressCollection addrs = iface->getAddresses();
|
||||||
|
|
||||||
|
for (IfaceMgr::AddressCollection::const_iterator addr = addrs.begin();
|
||||||
|
addr != addrs.end(); ++addr) {
|
||||||
|
if (addr->getFamily() != AF_INET) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
serverid_ = OptionPtr(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
|
||||||
|
*addr));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
isc_throw(BadValue, "No suitable interfaces for server-identifier found");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Dhcpv4Srv::writeServerID(const std::string& file_name) {
|
||||||
|
fstream f(file_name.c_str(), ios::out | ios::trunc);
|
||||||
|
if (!f.good()) {
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
f << srvidToString(getServerID());
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
string Dhcpv4Srv::srvidToString(const OptionPtr& srvid) {
|
||||||
|
if (!srvid) {
|
||||||
|
isc_throw(BadValue, "NULL pointer passed to srvidToString()");
|
||||||
|
}
|
||||||
|
boost::shared_ptr<Option4AddrLst> generated =
|
||||||
|
boost::dynamic_pointer_cast<Option4AddrLst>(srvid);
|
||||||
|
if (!srvid) {
|
||||||
|
isc_throw(BadValue, "Pointer to invalid option passed to srvidToString()");
|
||||||
|
}
|
||||||
|
|
||||||
|
Option4AddrLst::AddressContainer addrs = generated->getAddresses();
|
||||||
|
if (addrs.size() != 1) {
|
||||||
|
isc_throw(BadValue, "Malformed option passed to srvidToString(). "
|
||||||
|
<< "Expected to contain a single IPv4 address.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (addrs[0].toText());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dhcpv4Srv::copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer) {
|
void Dhcpv4Srv::copyDefaultFields(const Pkt4Ptr& question, Pkt4Ptr& answer) {
|
||||||
|
@@ -28,6 +28,15 @@
|
|||||||
namespace isc {
|
namespace isc {
|
||||||
namespace dhcp {
|
namespace dhcp {
|
||||||
|
|
||||||
|
/// @brief file name of a server-id file
|
||||||
|
///
|
||||||
|
/// Server must store its server identifier in persistent storage that must not
|
||||||
|
/// change between restarts. This is name of the file that is created in dataDir
|
||||||
|
/// (see isc::dhcp::CfgMgr::getDataDir()). It is a text file that uses
|
||||||
|
/// regular IPv4 address, e.g. 192.0.2.1. Server will create it during
|
||||||
|
/// first run and then use it afterwards.
|
||||||
|
static const char* SERVER_ID_FILE = "b10-dhcp4-serverid";
|
||||||
|
|
||||||
/// @brief DHCPv4 server service.
|
/// @brief DHCPv4 server service.
|
||||||
///
|
///
|
||||||
/// This singleton class represents DHCPv4 server. It contains all
|
/// This singleton class represents DHCPv4 server. It contains all
|
||||||
@@ -216,7 +225,32 @@ protected:
|
|||||||
///
|
///
|
||||||
/// @throws isc::Unexpected Failed to obtain server identifier (i.e. no
|
/// @throws isc::Unexpected Failed to obtain server identifier (i.e. no
|
||||||
// previously stored configuration and no network interfaces available)
|
// previously stored configuration and no network interfaces available)
|
||||||
void setServerID();
|
void generateServerID();
|
||||||
|
|
||||||
|
/// @brief attempts to load server-id from a file
|
||||||
|
///
|
||||||
|
/// Tries to load duid from a text file. If the load is successful,
|
||||||
|
/// it creates server-id option and stores it in serverid_ (to be used
|
||||||
|
/// later by getServerID()).
|
||||||
|
///
|
||||||
|
/// @param file_name name of the server-id file to load
|
||||||
|
/// @return true if load was successful, false otherwise
|
||||||
|
bool loadServerID(const std::string& file_name);
|
||||||
|
|
||||||
|
/// @brief attempts to write server-id to a file
|
||||||
|
/// Tries to write server-id content (stored in serverid_) to a text file.
|
||||||
|
///
|
||||||
|
/// @param file_name name of the server-id file to write
|
||||||
|
/// @return true if write was successful, false otherwise
|
||||||
|
bool writeServerID(const std::string& file_name);
|
||||||
|
|
||||||
|
/// @brief converts server-id to text
|
||||||
|
/// Converts content of server-id option to a text representation, e.g.
|
||||||
|
/// "192.0.2.1"
|
||||||
|
///
|
||||||
|
/// @param opt option that contains server-id
|
||||||
|
/// @return string representation
|
||||||
|
static std::string srvidToString(const OptionPtr& opt);
|
||||||
|
|
||||||
/// @brief Selects a subnet for a given client's packet.
|
/// @brief Selects a subnet for a given client's packet.
|
||||||
///
|
///
|
||||||
|
@@ -49,9 +49,15 @@ public:
|
|||||||
using Dhcpv4Srv::processDecline;
|
using Dhcpv4Srv::processDecline;
|
||||||
using Dhcpv4Srv::processInform;
|
using Dhcpv4Srv::processInform;
|
||||||
using Dhcpv4Srv::getServerID;
|
using Dhcpv4Srv::getServerID;
|
||||||
|
using Dhcpv4Srv::loadServerID;
|
||||||
|
using Dhcpv4Srv::generateServerID;
|
||||||
|
using Dhcpv4Srv::writeServerID;
|
||||||
using Dhcpv4Srv::sanityCheck;
|
using Dhcpv4Srv::sanityCheck;
|
||||||
|
using Dhcpv4Srv::srvidToString;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char* SRVID_FILE = "server-id-test.txt";
|
||||||
|
|
||||||
class Dhcpv4SrvTest : public ::testing::Test {
|
class Dhcpv4SrvTest : public ::testing::Test {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@@ -67,6 +73,9 @@ public:
|
|||||||
|
|
||||||
CfgMgr::instance().deleteSubnets4();
|
CfgMgr::instance().deleteSubnets4();
|
||||||
CfgMgr::instance().addSubnet4(subnet_);
|
CfgMgr::instance().addSubnet4(subnet_);
|
||||||
|
|
||||||
|
// it's ok if that fails. There should not be such a file anyway
|
||||||
|
unlink(SRVID_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief checks that the response matches request
|
/// @brief checks that the response matches request
|
||||||
@@ -245,6 +254,9 @@ public:
|
|||||||
|
|
||||||
~Dhcpv4SrvTest() {
|
~Dhcpv4SrvTest() {
|
||||||
CfgMgr::instance().deleteSubnets4();
|
CfgMgr::instance().deleteSubnets4();
|
||||||
|
|
||||||
|
// Let's clean up if there is such a file.
|
||||||
|
unlink(SRVID_FILE);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief A subnet used in most tests
|
/// @brief A subnet used in most tests
|
||||||
@@ -691,7 +703,7 @@ TEST_F(Dhcpv4SrvTest, ManyDiscovers) {
|
|||||||
checkAddressParams(offer2, subnet_);
|
checkAddressParams(offer2, subnet_);
|
||||||
checkAddressParams(offer3, subnet_);
|
checkAddressParams(offer3, subnet_);
|
||||||
|
|
||||||
// Check DUIDs
|
// Check server-ids
|
||||||
checkServerId(offer1, srv->getServerID());
|
checkServerId(offer1, srv->getServerID());
|
||||||
checkServerId(offer2, srv->getServerID());
|
checkServerId(offer2, srv->getServerID());
|
||||||
checkServerId(offer3, srv->getServerID());
|
checkServerId(offer3, srv->getServerID());
|
||||||
@@ -1126,4 +1138,34 @@ TEST_F(Dhcpv4SrvTest, ReleaseReject) {
|
|||||||
EXPECT_FALSE(l);
|
EXPECT_FALSE(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This test verifies if the server-id disk operations (read, write) are
|
||||||
|
// working properly.
|
||||||
|
TEST_F(Dhcpv4SrvTest, ServerID) {
|
||||||
|
NakedDhcpv4Srv srv(0);
|
||||||
|
|
||||||
|
string srvid_text = "192.0.2.100";
|
||||||
|
IOAddress srvid(srvid_text);
|
||||||
|
|
||||||
|
fstream file1(SRVID_FILE, ios::out | ios::trunc);
|
||||||
|
file1 << srvid_text;
|
||||||
|
file1.close();
|
||||||
|
|
||||||
|
// Test reading from a file
|
||||||
|
EXPECT_TRUE(srv.loadServerID(SRVID_FILE));
|
||||||
|
ASSERT_TRUE(srv.getServerID());
|
||||||
|
EXPECT_EQ(srvid_text, srv.srvidToString(srv.getServerID()));
|
||||||
|
|
||||||
|
// Now test writing to a file
|
||||||
|
EXPECT_EQ(0, unlink(SRVID_FILE));
|
||||||
|
EXPECT_NO_THROW(srv.writeServerID(SRVID_FILE));
|
||||||
|
|
||||||
|
fstream file2(SRVID_FILE, ios::in);
|
||||||
|
ASSERT_TRUE(file2.good());
|
||||||
|
string text;
|
||||||
|
file2 >> text;
|
||||||
|
file2.close();
|
||||||
|
|
||||||
|
EXPECT_EQ(srvid_text, text);
|
||||||
|
}
|
||||||
|
|
||||||
} // end of anonymous namespace
|
} // end of anonymous namespace
|
||||||
|
@@ -42,7 +42,6 @@ using namespace std;
|
|||||||
namespace isc {
|
namespace isc {
|
||||||
namespace dhcp {
|
namespace dhcp {
|
||||||
|
|
||||||
|
|
||||||
ControlledDhcpv6Srv* ControlledDhcpv6Srv::server_ = NULL;
|
ControlledDhcpv6Srv* ControlledDhcpv6Srv::server_ = NULL;
|
||||||
|
|
||||||
ConstElementPtr
|
ConstElementPtr
|
||||||
|
@@ -194,6 +194,34 @@ A debug message listing the data returned to the client.
|
|||||||
The IPv6 DHCP server has encountered a fatal error and is terminating.
|
The IPv6 DHCP server has encountered a fatal error and is terminating.
|
||||||
The reason for the failure is included in the message.
|
The reason for the failure is included in the message.
|
||||||
|
|
||||||
|
% DHCP6_SERVERID_GENERATED server-id %1 has been generated and will be stored in %2
|
||||||
|
This informational messages indicates that the server was not able to read
|
||||||
|
its server identifier (DUID) and has generated a new one. This server-id
|
||||||
|
will be stored in a file and will be read and used during next restart. It
|
||||||
|
is normal behavior when the server is started for the first time. If
|
||||||
|
this message is printed every start, please check that the server have
|
||||||
|
sufficient permission to write its server-id file and that the file is not
|
||||||
|
corrupt.
|
||||||
|
|
||||||
|
Changing the server identifier in a production environment is not
|
||||||
|
recommended as existing clients will not recognize the server and may go
|
||||||
|
through a rebind phase. However, they should be able to recover without
|
||||||
|
losing their leases.
|
||||||
|
|
||||||
|
% DHCP6_SERVERID_LOADED server-id %1 has been loaded from file %2
|
||||||
|
This debug message indicates that the server loaded its server identifier.
|
||||||
|
That value is sent in all server responses and clients use it to
|
||||||
|
discriminate between servers. This is a part of normal startup or
|
||||||
|
reconfiguration procedure.
|
||||||
|
|
||||||
|
% DHCP6_SERVERID_WRITE_FAIL server was not able to write its ID to file %1
|
||||||
|
This warning message indicates that server was not able to write its
|
||||||
|
server identifier (DUID) to a file. This likely indicates lack of write
|
||||||
|
permission to a given file or directory. This is not cricital and the
|
||||||
|
server will continue to operate, but server will generate different DUID
|
||||||
|
during every start and clients will need to go through a rebind phase
|
||||||
|
to recover.
|
||||||
|
|
||||||
% DHCP6_SESSION_FAIL failed to establish BIND 10 session (%1), running stand-alone
|
% DHCP6_SESSION_FAIL failed to establish BIND 10 session (%1), running stand-alone
|
||||||
The server has failed to establish communication with the rest of BIND
|
The server has failed to establish communication with the rest of BIND
|
||||||
10 and is running in stand-alone mode. (This behavior will change once
|
10 and is running in stand-alone mode. (This behavior will change once
|
||||||
|
@@ -36,11 +36,16 @@
|
|||||||
#include <exceptions/exceptions.h>
|
#include <exceptions/exceptions.h>
|
||||||
#include <util/io_utilities.h>
|
#include <util/io_utilities.h>
|
||||||
#include <util/range_utilities.h>
|
#include <util/range_utilities.h>
|
||||||
|
#include <util/encode/hex.h>
|
||||||
|
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
|
#include <boost/tokenizer.hpp>
|
||||||
|
#include <boost/algorithm/string/erase.hpp>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
using namespace isc;
|
using namespace isc;
|
||||||
using namespace isc::asiolink;
|
using namespace isc::asiolink;
|
||||||
@@ -70,7 +75,22 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port, const char* dbconfig)
|
|||||||
IfaceMgr::instance().openSockets6(port);
|
IfaceMgr::instance().openSockets6(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
setServerID();
|
string duid_file = CfgMgr::instance().getDataDir() + "/" + string(SERVER_DUID_FILE);
|
||||||
|
if (loadServerID(duid_file)) {
|
||||||
|
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_SERVERID_LOADED)
|
||||||
|
.arg(duid_file);
|
||||||
|
} else {
|
||||||
|
generateServerID();
|
||||||
|
LOG_INFO(dhcp6_logger, DHCP6_SERVERID_GENERATED)
|
||||||
|
.arg(duidToString(getServerID()))
|
||||||
|
.arg(duid_file);
|
||||||
|
|
||||||
|
if (!writeServerID(duid_file)) {
|
||||||
|
LOG_WARN(dhcp6_logger, DHCP6_SERVERID_WRITE_FAIL)
|
||||||
|
.arg(duid_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Instantiate LeaseMgr
|
// Instantiate LeaseMgr
|
||||||
LeaseMgrFactory::create(dbconfig);
|
LeaseMgrFactory::create(dbconfig);
|
||||||
@@ -209,10 +229,67 @@ bool Dhcpv6Srv::run() {
|
|||||||
return (true);
|
return (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dhcpv6Srv::setServerID() {
|
bool Dhcpv6Srv::loadServerID(const std::string& file_name) {
|
||||||
|
|
||||||
/// @todo: DUID should be generated once and then stored, rather
|
// load content of the file into a string
|
||||||
/// than generated each time
|
fstream f(file_name.c_str(), ios::in);
|
||||||
|
if (!f.is_open()) {
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
string hex_string;
|
||||||
|
f >> hex_string;
|
||||||
|
f.close();
|
||||||
|
|
||||||
|
// remove any spaces
|
||||||
|
boost::algorithm::erase_all(hex_string, " ");
|
||||||
|
|
||||||
|
// now remove :
|
||||||
|
/// @todo: We should check first if the format is sane.
|
||||||
|
/// Otherwise 1:2:3:4 will be converted to 0x12, 0x34
|
||||||
|
boost::algorithm::erase_all(hex_string, ":");
|
||||||
|
|
||||||
|
std::vector<uint8_t> bin;
|
||||||
|
|
||||||
|
// Decode the hex string and store it in bin (which happens
|
||||||
|
// to be OptionBuffer format)
|
||||||
|
isc::util::encode::decodeHex(hex_string, bin);
|
||||||
|
|
||||||
|
// Now create server-id option
|
||||||
|
serverid_.reset(new Option(Option::V6, D6O_SERVERID, bin));
|
||||||
|
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Dhcpv6Srv::duidToString(const OptionPtr& opt) {
|
||||||
|
stringstream tmp;
|
||||||
|
|
||||||
|
OptionBuffer data = opt->getData();
|
||||||
|
|
||||||
|
bool colon = false;
|
||||||
|
for (OptionBufferConstIter it = data.begin(); it != data.end(); ++it) {
|
||||||
|
if (colon) {
|
||||||
|
tmp << ":";
|
||||||
|
}
|
||||||
|
tmp << hex << setw(2) << setfill('0') << static_cast<uint16_t>(*it);
|
||||||
|
if (!colon) {
|
||||||
|
colon = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmp.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Dhcpv6Srv::writeServerID(const std::string& file_name) {
|
||||||
|
fstream f(file_name.c_str(), ios::out | ios::trunc);
|
||||||
|
if (!f.good()) {
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
f << duidToString(getServerID());
|
||||||
|
f.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Dhcpv6Srv::generateServerID() {
|
||||||
|
|
||||||
/// @todo: This code implements support for DUID-LLT (the recommended one).
|
/// @todo: This code implements support for DUID-LLT (the recommended one).
|
||||||
/// We should eventually add support for other DUID types: DUID-LL, DUID-EN
|
/// We should eventually add support for other DUID types: DUID-LL, DUID-EN
|
||||||
|
@@ -31,6 +31,16 @@
|
|||||||
namespace isc {
|
namespace isc {
|
||||||
namespace dhcp {
|
namespace dhcp {
|
||||||
|
|
||||||
|
/// @brief file name of a server-id file
|
||||||
|
///
|
||||||
|
/// Server must store its duid in persistent storage that must not change
|
||||||
|
/// between restarts. This is name of the file that is created in dataDir
|
||||||
|
/// (see isc::dhcp::CfgMgr::getDataDir()). It is a text file that uses
|
||||||
|
/// double digit hex values separated by colons format, e.g.
|
||||||
|
/// 01:ff:02:03:06:80:90:ab:cd:ef. Server will create it during first
|
||||||
|
/// run and then use it afterwards.
|
||||||
|
static const char* SERVER_DUID_FILE = "b10-dhcp6-serverid";
|
||||||
|
|
||||||
/// @brief DHCPv6 server service.
|
/// @brief DHCPv6 server service.
|
||||||
///
|
///
|
||||||
/// This class represents DHCPv6 server. It contains all
|
/// This class represents DHCPv6 server. It contains all
|
||||||
@@ -290,15 +300,39 @@ protected:
|
|||||||
|
|
||||||
/// @brief Sets server-identifier.
|
/// @brief Sets server-identifier.
|
||||||
///
|
///
|
||||||
/// This method attempts to set server-identifier DUID. It loads it
|
/// This method attempts to generate server-identifier DUID. It generates a
|
||||||
/// from a file. If file load fails, it generates new DUID using
|
/// new DUID using interface link-layer addresses (EUI-64) + timestamp (DUID
|
||||||
/// interface link-layer addresses (EUI-64) + timestamp (DUID type
|
/// type duid-llt, see RFC3315, section 9.2). If there are no suitable
|
||||||
/// duid-llt, see RFC3315, section 9.2). If there are no suitable
|
|
||||||
/// interfaces present, exception it thrown
|
/// interfaces present, exception it thrown
|
||||||
///
|
///
|
||||||
/// @throws isc::Unexpected Failed to read DUID file and no suitable
|
/// @throws isc::Unexpected Failed to read DUID file and no suitable
|
||||||
/// interfaces for new DUID generation are detected.
|
/// interfaces for new DUID generation are detected.
|
||||||
void setServerID();
|
void generateServerID();
|
||||||
|
|
||||||
|
/// @brief attempts to load DUID from a file
|
||||||
|
///
|
||||||
|
/// Tries to load duid from a text file. If the load is successful,
|
||||||
|
/// it creates server-id option and stores it in serverid_ (to be used
|
||||||
|
/// later by getServerID()).
|
||||||
|
///
|
||||||
|
/// @param file_name name of the DUID file to load
|
||||||
|
/// @return true if load was successful, false otherwise
|
||||||
|
bool loadServerID(const std::string& file_name);
|
||||||
|
|
||||||
|
/// @brief attempts to write DUID to a file
|
||||||
|
/// Tries to write duid content (stored in serverid_) to a text file.
|
||||||
|
///
|
||||||
|
/// @param file_name name of the DUID file to write
|
||||||
|
/// @return true if write was successful, false otherwise
|
||||||
|
bool writeServerID(const std::string& file_name);
|
||||||
|
|
||||||
|
/// @brief converts DUID to text
|
||||||
|
/// Converts content of DUID option to a text representation, e.g.
|
||||||
|
/// 01:ff:02:03:06:80:90:ab:cd:ef
|
||||||
|
///
|
||||||
|
/// @param opt option that contains DUID
|
||||||
|
/// @return string representation
|
||||||
|
static std::string duidToString(const OptionPtr& opt);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// @brief Allocation Engine.
|
/// @brief Allocation Engine.
|
||||||
|
@@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
#include <boost/scoped_ptr.hpp>
|
#include <boost/scoped_ptr.hpp>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -64,10 +64,16 @@ public:
|
|||||||
using Dhcpv6Srv::createStatusCode;
|
using Dhcpv6Srv::createStatusCode;
|
||||||
using Dhcpv6Srv::selectSubnet;
|
using Dhcpv6Srv::selectSubnet;
|
||||||
using Dhcpv6Srv::sanityCheck;
|
using Dhcpv6Srv::sanityCheck;
|
||||||
|
using Dhcpv6Srv::loadServerID;
|
||||||
|
using Dhcpv6Srv::writeServerID;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char* DUID_FILE = "server-id-test.txt";
|
||||||
|
|
||||||
class Dhcpv6SrvTest : public ::testing::Test {
|
class Dhcpv6SrvTest : public ::testing::Test {
|
||||||
public:
|
public:
|
||||||
|
/// Name of the server-id file (used in server-id tests)
|
||||||
|
|
||||||
// these are empty for now, but let's keep them around
|
// these are empty for now, but let's keep them around
|
||||||
Dhcpv6SrvTest() : rcode_(-1) {
|
Dhcpv6SrvTest() : rcode_(-1) {
|
||||||
subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 48, 1000,
|
subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 48, 1000,
|
||||||
@@ -77,6 +83,9 @@ public:
|
|||||||
|
|
||||||
CfgMgr::instance().deleteSubnets6();
|
CfgMgr::instance().deleteSubnets6();
|
||||||
CfgMgr::instance().addSubnet6(subnet_);
|
CfgMgr::instance().addSubnet6(subnet_);
|
||||||
|
|
||||||
|
// it's ok if that fails. There should not be such a file anyway
|
||||||
|
unlink(DUID_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate IA_NA option with specified parameters
|
// Generate IA_NA option with specified parameters
|
||||||
@@ -246,6 +255,9 @@ public:
|
|||||||
|
|
||||||
~Dhcpv6SrvTest() {
|
~Dhcpv6SrvTest() {
|
||||||
CfgMgr::instance().deleteSubnets6();
|
CfgMgr::instance().deleteSubnets6();
|
||||||
|
|
||||||
|
// Let's clean up if there is such a file.
|
||||||
|
unlink(DUID_FILE);
|
||||||
};
|
};
|
||||||
|
|
||||||
// A subnet used in most tests
|
// A subnet used in most tests
|
||||||
@@ -1407,7 +1419,38 @@ TEST_F(Dhcpv6SrvTest, selectSubnetIface) {
|
|||||||
|
|
||||||
pkt->setIface("wifi1");
|
pkt->setIface("wifi1");
|
||||||
EXPECT_EQ(subnet3, srv.selectSubnet(pkt));
|
EXPECT_EQ(subnet3, srv.selectSubnet(pkt));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test verifies if the server-id disk operations (read, write) are
|
||||||
|
// working properly.
|
||||||
|
TEST_F(Dhcpv6SrvTest, ServerID) {
|
||||||
|
NakedDhcpv6Srv srv(0);
|
||||||
|
|
||||||
|
string duid1_text = "01:ff:02:03:06:80:90:ab:cd:ef";
|
||||||
|
uint8_t duid1[] = { 0x01, 0xff, 2, 3, 6, 0x80, 0x90, 0xab, 0xcd, 0xef };
|
||||||
|
OptionBuffer expected_duid1(duid1, duid1 + sizeof(duid1));
|
||||||
|
|
||||||
|
fstream file1(DUID_FILE, ios::out | ios::trunc);
|
||||||
|
file1 << duid1_text;
|
||||||
|
file1.close();
|
||||||
|
|
||||||
|
// Test reading from a file
|
||||||
|
EXPECT_TRUE(srv.loadServerID(DUID_FILE));
|
||||||
|
ASSERT_TRUE(srv.getServerID());
|
||||||
|
ASSERT_EQ(sizeof(duid1) + Option::OPTION6_HDR_LEN, srv.getServerID()->len());
|
||||||
|
ASSERT_TRUE(expected_duid1 == srv.getServerID()->getData());
|
||||||
|
|
||||||
|
// Now test writing to a file
|
||||||
|
EXPECT_EQ(0, unlink(DUID_FILE));
|
||||||
|
EXPECT_NO_THROW(srv.writeServerID(DUID_FILE));
|
||||||
|
|
||||||
|
fstream file2(DUID_FILE, ios::in);
|
||||||
|
ASSERT_TRUE(file2.good());
|
||||||
|
string text;
|
||||||
|
file2 >> text;
|
||||||
|
file2.close();
|
||||||
|
|
||||||
|
EXPECT_EQ(duid1_text, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
|
/// @todo: Add more negative tests for processX(), e.g. extend sanityCheck() test
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
SUBDIRS = . tests
|
SUBDIRS = . tests
|
||||||
|
|
||||||
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
|
AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib -DDHCP_DATA_DIR="\"$(localstatedir)\""
|
||||||
AM_CPPFLAGS += $(BOOST_INCLUDES)
|
AM_CPPFLAGS += $(BOOST_INCLUDES)
|
||||||
if HAVE_MYSQL
|
if HAVE_MYSQL
|
||||||
AM_CPPFLAGS += $(MYSQL_CPPFLAGS)
|
AM_CPPFLAGS += $(MYSQL_CPPFLAGS)
|
||||||
|
@@ -248,7 +248,16 @@ void CfgMgr::deleteSubnets6() {
|
|||||||
subnets6_.clear();
|
subnets6_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
CfgMgr::CfgMgr() {
|
std::string CfgMgr::getDataDir() {
|
||||||
|
return (datadir_);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CfgMgr::CfgMgr()
|
||||||
|
:datadir_(DHCP_DATA_DIR) {
|
||||||
|
// DHCP_DATA_DIR must be set set with -DDHCP_DATA_DIR="..." in Makefile.am
|
||||||
|
// Note: the definition of DHCP_DATA_DIR needs to include quotation marks
|
||||||
|
// See AM_CPPFLAGS definition in Makefile.am
|
||||||
}
|
}
|
||||||
|
|
||||||
CfgMgr::~CfgMgr() {
|
CfgMgr::~CfgMgr() {
|
||||||
|
@@ -230,6 +230,14 @@ public:
|
|||||||
/// completely new?
|
/// completely new?
|
||||||
void deleteSubnets4();
|
void deleteSubnets4();
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief returns path do the data directory
|
||||||
|
///
|
||||||
|
/// This method returns a path to writeable directory that DHCP servers
|
||||||
|
/// can store data in.
|
||||||
|
/// @return data directory
|
||||||
|
std::string getDataDir();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
/// @brief Protected constructor.
|
/// @brief Protected constructor.
|
||||||
@@ -274,6 +282,8 @@ private:
|
|||||||
/// @brief Container for defined DHCPv4 option spaces.
|
/// @brief Container for defined DHCPv4 option spaces.
|
||||||
OptionSpaceCollection spaces4_;
|
OptionSpaceCollection spaces4_;
|
||||||
|
|
||||||
|
/// @brief directory where data files (e.g. server-id) are stored
|
||||||
|
std::string datadir_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace isc::dhcp
|
} // namespace isc::dhcp
|
||||||
|
Reference in New Issue
Block a user