diff --git a/ChangeLog b/ChangeLog
index ce45dbc68b..86eaa0eaef 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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"
diff --git a/doc/guide/bind10-guide.xml b/doc/guide/bind10-guide.xml
index f1f5859d2c..5384d1457c 100644
--- a/doc/guide/bind10-guide.xml
+++ b/doc/guide/bind10-guide.xml
@@ -3485,18 +3485,34 @@ Dhcp4/subnet4 [] list (default)
src/bin/dhcp6/dhcp4_srv.cc file, modify the following parameters and
recompile:
-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";
+const std::string HARDCODED_DOMAIN_NAME = "isc.example.com";
Lease database and configuration support is planned for end of 2012.
+
+ Server Identifier in DHCPv4
+ 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.
+
+
+ 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.
+
+
+
Supported standards
The following standards and draft standards are currently
@@ -3841,6 +3857,31 @@ Dhcp6/subnet6 [] list (default)
+
+ Server Identifier in DHCPv6
+ 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.
+
+
+ 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.
+
+
+
Supported DHCPv6 Standards
The following standards and draft standards are currently
diff --git a/src/bin/dhcp4/dhcp4_messages.mes b/src/bin/dhcp4/dhcp4_messages.mes
index fc4782382a..02ad0a075a 100644
--- a/src/bin/dhcp4/dhcp4_messages.mes
+++ b/src/bin/dhcp4/dhcp4_messages.mes
@@ -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
diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc
index 88ccfead7c..20d8597c74 100644
--- a/src/bin/dhcp4/dhcp4_srv.cc
+++ b/src/bin/dhcp4/dhcp4_srv.cc
@@ -30,6 +30,11 @@
#include
#include
+#include
+
+#include
+#include
+
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 generated =
+ boost::dynamic_pointer_cast(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) {
diff --git a/src/bin/dhcp4/dhcp4_srv.h b/src/bin/dhcp4/dhcp4_srv.h
index 53401c5e18..8d26e0580e 100644
--- a/src/bin/dhcp4/dhcp4_srv.h
+++ b/src/bin/dhcp4/dhcp4_srv.h
@@ -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.
///
diff --git a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
index bc2246fccb..a2331ffdc0 100644
--- a/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
+++ b/src/bin/dhcp4/tests/dhcp4_srv_unittest.cc
@@ -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
diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
index ba3e2c2691..5505a3607d 100644
--- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc
+++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc
@@ -42,7 +42,6 @@ using namespace std;
namespace isc {
namespace dhcp {
-
ControlledDhcpv6Srv* ControlledDhcpv6Srv::server_ = NULL;
ConstElementPtr
diff --git a/src/bin/dhcp6/dhcp6_messages.mes b/src/bin/dhcp6/dhcp6_messages.mes
index 83f75d952f..c38776ca45 100644
--- a/src/bin/dhcp6/dhcp6_messages.mes
+++ b/src/bin/dhcp6/dhcp6_messages.mes
@@ -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
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 8fb55eceb5..e50950f836 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -36,11 +36,16 @@
#include
#include
#include
+#include
#include
+#include
+#include
#include
#include
+#include
+#include
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 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(*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
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index 30abb500af..6515543f73 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -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.
diff --git a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
index 0ed4e85093..1df143fff4 100644
--- a/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
+++ b/src/bin/dhcp6/tests/dhcp6_srv_unittest.cc
@@ -35,7 +35,7 @@
#include
#include
-
+#include
#include
#include
#include
@@ -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
diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am
index cc5daec457..ce066e7fe1 100644
--- a/src/lib/dhcpsrv/Makefile.am
+++ b/src/lib/dhcpsrv/Makefile.am
@@ -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)
diff --git a/src/lib/dhcpsrv/cfgmgr.cc b/src/lib/dhcpsrv/cfgmgr.cc
index c6a190f6a6..b5e83e34ad 100644
--- a/src/lib/dhcpsrv/cfgmgr.cc
+++ b/src/lib/dhcpsrv/cfgmgr.cc
@@ -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() {
diff --git a/src/lib/dhcpsrv/cfgmgr.h b/src/lib/dhcpsrv/cfgmgr.h
index bc4ffdee5d..0e56869ca0 100644
--- a/src/lib/dhcpsrv/cfgmgr.h
+++ b/src/lib/dhcpsrv/cfgmgr.h
@@ -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