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