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 critical 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_USING_SERVERID server is using server-id %1 and stores in the the file %2
+This info message is logged when the server reads its server-id from a
+file or generates it. This message is a notification to the administrator
+what server-id will be used and where it is persisted. Typically, there is
+no need to modify the server id. However, it is possible to do it in the
+Kea configuration file. It is important to understand the implications of
+such modification. The clients will remember previous server-id, and will
+use it to extend their leases. As a result, they will have to go through
+a rebinding phase to re-acquire their leases and associate them with a
+new server id.
% DHCP6_SERVER_FAILED server failed: %1
The IPv6 DHCP server has encountered a fatal error and is terminating.
#include <dhcp/dhcp6.h>
#include <dhcp/docsis3_option_defs.h>
#include <dhcp/duid.h>
+#include <dhcp/duid_factory.h>
#include <dhcp/iface_mgr.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/option6_addrlst.h>
}
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(duidToString(getServerID()))
- .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);
- }
- }
+ DUIDFactory duid_factory(duid_file);
+ DuidPtr duid = duid_factory.get();
+ serverid_.reset(new Option(Option::V6, D6O_SERVERID, duid->getDuid()));
+ LOG_INFO(dhcp6_logger, DHCP6_USING_SERVERID)
+ .arg(duidToString(getServerID()))
+ .arg(duid_file);
// Instantiate allocation engine. The number of allocation attempts equal
// to zero indicates that the allocation engine will use the number of
return (true);
}
-bool Dhcpv6Srv::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, " ");
-
- // 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;
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();
- return (true);
-}
-
-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
- /// and DUID-UUID
-
- const IfaceMgr::IfaceCollection& ifaces = IfaceMgr::instance().getIfaces();
-
- // Let's find suitable interface.
- BOOST_FOREACH(IfacePtr iface, ifaces) {
- // All the following checks could be merged into one multi-condition
- // statement, but let's keep them separated as perhaps one day
- // we will grow knobs to selectively turn them on or off. Also,
- // this code is used only *once* during first start on a new machine
- // and then server-id is stored. (or at least it will be once
- // DUID storage is implemented)
-
- // I wish there was a this_is_a_real_physical_interface flag...
-
- // MAC address should be at least 6 bytes. Although there is no such
- // requirement in any RFC, all decent physical interfaces (Ethernet,
- // WiFi, InfiniBand, etc.) have 6 bytes long MAC address. We want to
- // base our DUID on real hardware address, rather than virtual
- // interface that pretends that underlying IP address is its MAC.
- if (iface->getMacLen() < MIN_MAC_LEN) {
- continue;
- }
-
- // 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;
- }
-
- // Some interfaces (like lo on Linux) report 6-bytes long
- // MAC address 00:00:00:00:00:00. Let's not use such weird interfaces
- // to generate DUID.
- if (isRangeZero(iface->getMac(), iface->getMac() + iface->getMacLen())) {
- continue;
- }
-
- // Ok, we have useful MAC. Let's generate DUID-LLT based on
- // it. See RFC3315, Section 9.2 for details.
-
- // DUID uses seconds since midnight of 01-01-2000, time() returns
- // seconds since 01-01-1970. DUID_TIME_EPOCH substitution corrects
- // that.
- time_t seconds = time(NULL);
- seconds -= DUID_TIME_EPOCH;
-
- OptionBuffer srvid(8 + iface->getMacLen());
- // We know that the buffer is more than 8 bytes long at this point.
- writeUint16(DUID::DUID_LLT, &srvid[0], 2);
- writeUint16(HWTYPE_ETHERNET, &srvid[2], 2);
- writeUint32(static_cast<uint32_t>(seconds), &srvid[4], 4);
- memcpy(&srvid[8], iface->getMac(), iface->getMacLen());
-
- serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
- srvid.begin(), srvid.end()));
- return;
- }
-
- // If we reached here, there are no suitable interfaces found.
- // Either interface detection is not supported on this platform or
- // this is really weird box. Let's use DUID-EN instead.
- // See Section 9.3 of RFC3315 for details.
-
- OptionBuffer srvid(12);
- writeUint16(DUID::DUID_EN, &srvid[0], srvid.size());
- writeUint32(ENTERPRISE_ID_ISC, &srvid[2], srvid.size() - 2);
-
- // Length of the identifier is company specific. I hereby declare
- // ISC "standard" of 6 bytes long pseudo-random numbers.
- srandom(time(NULL));
- fillRandom(&srvid[6], &srvid[12]);
-
- serverid_ = OptionPtr(new Option(Option::V6, D6O_SERVERID,
- srvid.begin(), srvid.end()));
-}
-
void
Dhcpv6Srv::copyClientOptions(const Pkt6Ptr& question, Pkt6Ptr& answer) {
// Add client-id.
void releaseLeases(const Pkt6Ptr& release, Pkt6Ptr& reply,
AllocEngine::ClientContext6& ctx);
- /// @brief Sets server-identifier.
- ///
- /// 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 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
/// @return string representation
static std::string duidToString(const OptionPtr& opt);
-
/// @brief dummy wrapper around IfaceMgr::receive6
///
/// This method is useful for testing purposes, where its replacement
EXPECT_EQ(subnet2, 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, remove(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);
-}
-
// Checks if server responses are sent to the proper port.
TEST_F(Dhcpv6SrvTest, portsDirectTraffic) {