mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-30 05:27:55 +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:
commit
6167f31e01
@ -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
|
||||
b10-dhcp6: It is now possible to specify that a configured subnet
|
||||
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
|
||||
recompile:
|
||||
<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_DNS_SERVER = "192.0.2.2";
|
||||
const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";
|
||||
const std::string HARDCODED_SERVER_ID = "192.0.2.1";</screen>
|
||||
const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";</screen>
|
||||
|
||||
Lease database and configuration support is planned for end of 2012.
|
||||
</para>
|
||||
</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">
|
||||
<title>Supported standards</title>
|
||||
<para>The following standards and draft standards are currently
|
||||
@ -3841,6 +3857,31 @@ Dhcp6/subnet6 [] list (default)</screen>
|
||||
</note>
|
||||
</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">
|
||||
<title>Supported DHCPv6 Standards</title>
|
||||
<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 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
|
||||
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
|
||||
|
@ -30,6 +30,11 @@
|
||||
#include <dhcpsrv/utils.h>
|
||||
#include <dhcpsrv/addr_utilities.h>
|
||||
|
||||
#include <boost/algorithm/string/erase.hpp>
|
||||
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
|
||||
using namespace isc;
|
||||
using namespace isc::asiolink;
|
||||
using namespace isc::dhcp;
|
||||
@ -41,7 +46,6 @@ using namespace std;
|
||||
const std::string HARDCODED_GATEWAY = "192.0.2.1";
|
||||
const std::string HARDCODED_DNS_SERVER = "192.0.2.2";
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
Dhcpv4Srv::setServerID() {
|
||||
/// @todo: implement this for real (see ticket #2588)
|
||||
serverid_ = OptionPtr(new Option4AddrLst(DHO_DHCP_SERVER_IDENTIFIER,
|
||||
IOAddress(HARDCODED_SERVER_ID)));
|
||||
bool Dhcpv4Srv::loadServerID(const std::string& file_name) {
|
||||
|
||||
// load content of the file into a string
|
||||
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, " ");
|
||||
|
||||
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) {
|
||||
|
@ -28,6 +28,15 @@
|
||||
namespace isc {
|
||||
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.
|
||||
///
|
||||
/// 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
|
||||
// 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.
|
||||
///
|
||||
|
@ -49,9 +49,15 @@ public:
|
||||
using Dhcpv4Srv::processDecline;
|
||||
using Dhcpv4Srv::processInform;
|
||||
using Dhcpv4Srv::getServerID;
|
||||
using Dhcpv4Srv::loadServerID;
|
||||
using Dhcpv4Srv::generateServerID;
|
||||
using Dhcpv4Srv::writeServerID;
|
||||
using Dhcpv4Srv::sanityCheck;
|
||||
using Dhcpv4Srv::srvidToString;
|
||||
};
|
||||
|
||||
static const char* SRVID_FILE = "server-id-test.txt";
|
||||
|
||||
class Dhcpv4SrvTest : public ::testing::Test {
|
||||
public:
|
||||
|
||||
@ -67,6 +73,9 @@ public:
|
||||
|
||||
CfgMgr::instance().deleteSubnets4();
|
||||
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
|
||||
@ -245,6 +254,9 @@ public:
|
||||
|
||||
~Dhcpv4SrvTest() {
|
||||
CfgMgr::instance().deleteSubnets4();
|
||||
|
||||
// Let's clean up if there is such a file.
|
||||
unlink(SRVID_FILE);
|
||||
};
|
||||
|
||||
/// @brief A subnet used in most tests
|
||||
@ -691,7 +703,7 @@ TEST_F(Dhcpv4SrvTest, ManyDiscovers) {
|
||||
checkAddressParams(offer2, subnet_);
|
||||
checkAddressParams(offer3, subnet_);
|
||||
|
||||
// Check DUIDs
|
||||
// Check server-ids
|
||||
checkServerId(offer1, srv->getServerID());
|
||||
checkServerId(offer2, srv->getServerID());
|
||||
checkServerId(offer3, srv->getServerID());
|
||||
@ -1126,4 +1138,34 @@ TEST_F(Dhcpv4SrvTest, ReleaseReject) {
|
||||
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
|
||||
|
@ -42,7 +42,6 @@ using namespace std;
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
|
||||
ControlledDhcpv6Srv* ControlledDhcpv6Srv::server_ = NULL;
|
||||
|
||||
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 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
|
||||
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
|
||||
|
@ -36,11 +36,16 @@
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <util/io_utilities.h>
|
||||
#include <util/range_utilities.h>
|
||||
#include <util/encode/hex.h>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/tokenizer.hpp>
|
||||
#include <boost/algorithm/string/erase.hpp>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
|
||||
using namespace isc;
|
||||
using namespace isc::asiolink;
|
||||
@ -70,7 +75,22 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port, const char* dbconfig)
|
||||
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
|
||||
LeaseMgrFactory::create(dbconfig);
|
||||
@ -209,10 +229,67 @@ bool Dhcpv6Srv::run() {
|
||||
return (true);
|
||||
}
|
||||
|
||||
void Dhcpv6Srv::setServerID() {
|
||||
bool Dhcpv6Srv::loadServerID(const std::string& file_name) {
|
||||
|
||||
/// @todo: DUID should be generated once and then stored, rather
|
||||
/// than generated each time
|
||||
// load content of the file into a string
|
||||
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).
|
||||
/// We should eventually add support for other DUID types: DUID-LL, DUID-EN
|
||||
|
@ -31,6 +31,16 @@
|
||||
namespace isc {
|
||||
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.
|
||||
///
|
||||
/// This class represents DHCPv6 server. It contains all
|
||||
@ -290,15 +300,39 @@ protected:
|
||||
|
||||
/// @brief Sets server-identifier.
|
||||
///
|
||||
/// This method attempts to set server-identifier DUID. It loads it
|
||||
/// from a file. If file load fails, it generates new DUID using
|
||||
/// interface link-layer addresses (EUI-64) + timestamp (DUID type
|
||||
/// duid-llt, see RFC3315, section 9.2). If there are no suitable
|
||||
/// This method attempts to generate server-identifier DUID. It generates a
|
||||
/// new DUID using interface link-layer addresses (EUI-64) + timestamp (DUID
|
||||
/// type duid-llt, see RFC3315, section 9.2). If there are no suitable
|
||||
/// interfaces present, exception it thrown
|
||||
///
|
||||
/// @throws isc::Unexpected Failed to read DUID file and no suitable
|
||||
/// 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:
|
||||
/// @brief Allocation Engine.
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
@ -64,10 +64,16 @@ public:
|
||||
using Dhcpv6Srv::createStatusCode;
|
||||
using Dhcpv6Srv::selectSubnet;
|
||||
using Dhcpv6Srv::sanityCheck;
|
||||
using Dhcpv6Srv::loadServerID;
|
||||
using Dhcpv6Srv::writeServerID;
|
||||
};
|
||||
|
||||
static const char* DUID_FILE = "server-id-test.txt";
|
||||
|
||||
class Dhcpv6SrvTest : public ::testing::Test {
|
||||
public:
|
||||
/// Name of the server-id file (used in server-id tests)
|
||||
|
||||
// these are empty for now, but let's keep them around
|
||||
Dhcpv6SrvTest() : rcode_(-1) {
|
||||
subnet_ = Subnet6Ptr(new Subnet6(IOAddress("2001:db8:1::"), 48, 1000,
|
||||
@ -77,6 +83,9 @@ public:
|
||||
|
||||
CfgMgr::instance().deleteSubnets6();
|
||||
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
|
||||
@ -246,6 +255,9 @@ public:
|
||||
|
||||
~Dhcpv6SrvTest() {
|
||||
CfgMgr::instance().deleteSubnets6();
|
||||
|
||||
// Let's clean up if there is such a file.
|
||||
unlink(DUID_FILE);
|
||||
};
|
||||
|
||||
// A subnet used in most tests
|
||||
@ -1407,7 +1419,38 @@ TEST_F(Dhcpv6SrvTest, selectSubnetIface) {
|
||||
|
||||
pkt->setIface("wifi1");
|
||||
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
|
||||
|
@ -1,6 +1,6 @@
|
||||
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)
|
||||
if HAVE_MYSQL
|
||||
AM_CPPFLAGS += $(MYSQL_CPPFLAGS)
|
||||
|
@ -248,7 +248,16 @@ void CfgMgr::deleteSubnets6() {
|
||||
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() {
|
||||
|
@ -230,6 +230,14 @@ public:
|
||||
/// completely new?
|
||||
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:
|
||||
|
||||
/// @brief Protected constructor.
|
||||
@ -274,6 +282,8 @@ private:
|
||||
/// @brief Container for defined DHCPv4 option spaces.
|
||||
OptionSpaceCollection spaces4_;
|
||||
|
||||
/// @brief directory where data files (e.g. server-id) are stored
|
||||
std::string datadir_;
|
||||
};
|
||||
|
||||
} // namespace isc::dhcp
|
||||
|
Loading…
x
Reference in New Issue
Block a user