Commit faf52b06 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[master] Merge branch 'trac3874a'

parents ed4386f6 49de8971
# This is an example configuration file for DHCPv6 server in Kea.
# It demonstrates how to configure Kea to use DUID-LLT with some
# values specified explicitly.
{ "Dhcp6":
{
# Configure server identifier (DUID-LLT). The hexadecimal value of the
# identifier will be used as link layer address component of the DUID.
# The link layer type will be ethernet. The value of time is set to 0
# which indicates that the server must generate this value, i.e. use
# current time. Note that it is easy to move from this configuration
# to DUID-EN or DUID-LL. It would require changing the "type" value
# to "EN" or "LL" respectively. The "identifier" would hold a
# DUID-EN variable length identifier or DUID-LL link layer address.
# The values of "time" and "htype" would be ignored for DUID-EN.
# If one wanted to use a non-default enterprise-id for DUID-EN, the
# "enterprise-id" parameter would need to be added. Note that only
# a "type" parameter is mandatory while specifying "server-id" map.
"server-id": {
"type": "LLT",
"identifier": "12C4D5AF870C",
"time": 0,
"htype": 1
},
# Kea is told to listen on ethX interface only.
"interfaces-config": {
"interfaces": [ "ethX" ]
},
# We need to specify lease type. As of May 2014, three backends are supported:
# memfile, mysql and pgsql. We'll just use memfile, because it doesn't require
# any prior set up.
"lease-database": {
"type": "memfile"
},
# Addresses will be assigned with preferred and valid lifetimes
# being 3000 and 4000, respectively. Client is told to start
# renewing after 1000 seconds. If the server does not respond
# after 2000 seconds since the lease was granted, client is supposed
# to start REBIND procedure (emergency renewal that allows switching
# to a different server).
"preferred-lifetime": 3000,
"valid-lifetime": 4000,
"renew-timer": 1000,
"rebind-timer": 2000,
# The following list defines subnets. Each subnet consists of at
# least subnet and pool entries.
"subnet6": [
{
"pools": [ { "pool": "2001:db8:1::/80" } ],
"subnet": "2001:db8:1::/64",
"interface": "ethX"
}
]
},
# The following configures logging. Kea will log all debug messages
# to /var/log/kea-debug.log file.
"Logging": {
"loggers": [
{
"name": "kea-dhcp6",
"output_options": [
{
"output": "/var/log/kea-debug.log"
}
],
"debuglevel": 0,
"severity": "INFO"
}
]
}
}
......@@ -2393,27 +2393,212 @@ should include options from the isc option space:
<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 <ulink url="http://tools.ietf.org/html/rfc3315">RFC 3315</ulink> 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. That file is read by the server
and the contained value used whenever the server is subsequently started.
</para>
<para>
It is unlikely that this parameter should ever need to be changed.
However, if such a need arises, stop the server, edit the file and restart
the server. (The file is named kea-dhcp6-serverid and by default is
stored in the "var" subdirectory of the directory in which Kea is installed.
This can be changed when Kea is built by using "--localstatedir"
on the "configure" command line.) The file 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.
servers present on the same link.
<ulink url="http://tools.ietf.org/html/rfc3315">RFC 3315</ulink>
defines three DUID types: DUID-LLT, DUID-EN and DUID-LL.
<ulink url="http://tools.ietf.org/html/rfc6355">RFC 6355</ulink>
also defines DUID-UUID. Future specifications may introduce new
DUID types.</para>
<para>Kea DHCPv6 server generates a server identifier once, upon
the first startup, and stores it in a file. This identifier isn't
modified across restarts of the server (stable identifier).</para>
<para>Kea follows recommendation from
<ulink url="http://tools.ietf.org/html/rfc3315">RFC 3315</ulink>
to use DUID-LLT as a default server identifier. However, we have
received reports that some deployments require different DUID
types, and there is a need to administratively select both DUID
type and/or its contents.</para>
<para>The server identifier can be configured using parameters
within the <command>server-id</command> map element in the global
scope of the Kea configuration file. The following example
demonstrates how to select DUID-EN as a server identifier:
<screen>
"Dhcp6": {
"server-id": {
"type": "EN"
},
...
}
</screen>
</para>
<para>Currently supported values for <command>type</command>
parameter are: "LLT", "EN" and "LL", for DUID-LLT, DUID-EN and
DUID-LL respectively.</para>
<para>When a new DUID type is selected the server will generate its
value and replace any existing DUID in the file. The server will
use the new server identifier in all future interactions with the
clients.</para>
<note><para>If the new server identifier is created after some clients
have obtained their leases, the clients using old identifier will not
be able to renew the leases. The server will ignore messages
containing the old server identifier. Clients will continue sending
Renew until they transition to rebinding state. In this state they
will start sending Rebind messages to multicast address and without
a server identifier. The server will respond to the Rebind messages
with a new server identifier and the clients will associate the
new server identifier with their leases. Although the clients will
be able to keep their leases and will eventually learn the new server
identifier, this will be at the cost of increased number of renewals
and multicast traffic due to a need to rebind. Therefore it is
recommended to avoid modification of the server identifier type
and its value if the server has already assigned leases and these
leases are still valid.</para></note>
<para>There are cases when an administrator needs to explicitly
specify a DUID value, rather than allow the server to generate it.
The following example demonstrates how to explicitly set all
components of a DUID-LLT.
<screen>
"Dhcp6": {
"server-id": {
"type": "LLT",
"htype": 8,
"identifier": "A65DC7410F05",
"time": 2518920166
},
...
}
</screen>
where:
<itemizedlist>
<listitem><simpara><command>htype</command> is a 16-bit unsigned value
specifying hardware type,</simpara></listitem>
<listitem><simpara><command>identifier</command> is a link layer
address, specified as a string of hexadecimal digits,</simpara>
</listitem>
<listitem><simpara><command>time</command> is a 32-bit unsigned
time value.</simpara></listitem>
</itemizedlist>
</para>
<para>The hexadecimal representation of the DUID generated as a result
of the configuration specified above will be:
<screen>
00:01:00:08:96:23:AB:E6:A6:5D:C7:41:0F:05
------------------------------------------
|type|htype| time | identifier |
</screen>
</para>
<para>It is allowed to use special value of 0 for "htype" and "time",
which indicates that the server should use ANY value for these
components. If the server already uses a DUID-LLT it will use the
values from this DUID. If the server uses a DUID of a different type
or doesn't use any DUID yet, it will generate these values.
Similarly, if the "identifier" is assigned an empty string, the
value of the identifier will be generated. Omitting any of these
parameters is equivalent to setting them to those special values.
</para>
<para>For example, the following configuration:
<screen>
"Dhcp6": {
"server-id": {
"type": "LLT",
"htype": 0,
"identifier": "",
"time": 2518920166
},
...
}
</screen>
indicates that the server should use ANY link layer address and
hardware type. If the server is already using DUID-LLT it will
use link layer address and hardware type from the existing DUID.
If the server is not using any DUID yet, it will use link layer
address and hardware type from one of the available network
interfaces. The server will use explicit value of time. If it
is different than a time value present in the currently used
DUID, this value will be replaced. This will effectively cause
modification of the current server identifier.
</para>
<para>
The following example demonstrates an explicit configuration of
a DUID-EN:
<screen>
"Dhcp6": {
"server-id": {
"type": "EN",
"enterprise-id": 2495,
"identifier": "87ABEF7A5BB545",
},
...
}
</screen>
where:
<itemizedlist>
<listitem><simpara><command>enterprise-id</command> is a 32-bit
unsigned value holding enterprise number,</simpara></listitem>
<listitem><simpara><command>identifier</command> is a variable
length identifier within DUID-EN.</simpara></listitem>
</itemizedlist>
</para>
<para>
The hexadecimal representation of the DUID-EN created accoring to
the configuration above is:
<screen>
00:02:00:00:09:BF:87:AB:EF:7A:5B:B5:45
--------------------------------------
|type| ent-id | identifier |
</screen>
</para>
<para>As in the case of the DUID-LLT, special values can be used for the
configuration of the DUID-EN. If the "enterprise-id" is 0, the server
will use a value from the existing DUID-EN. If the server is not using
any DUID or the existing DUID has a different type, the ISC enterprise
id will be used. When an empty string is used for "identifier", the
identifier from the existing DUID-EN will be used. If the server is
not using any DUID-EN the new 6-bytes long identifier will be generated.
</para>
<para>DUID-LL is configured in the same way as DUID-LLT with an exception
that the <command>time</command> parameter has no effect for DUID-LL,
because this DUID type only comprises a hardware type and link layer
address. The following example demonstrates how to configure DUID-LL:
<screen>
"Dhcp6": {
"server-id": {
"type": "LL",
"htype": 8,
"identifier": "A65DC7410F05",
},
...
}
</screen>
</para>
<para>
which will result in the following server identifier:
<screen>
00:03:00:08:A6:5D:C7:41:0F:05
------------------------------
|type|htype| identifier |
</screen>
</para>
<para>Server stores a generated server identifier in the following
location: <userinput>[kea-install-dir]/var/kea/kea-dhcp6-serverid
</userinput>.
</para>
</section>
<section id="stateless-dhcp6">
......
......@@ -28,6 +28,13 @@ using namespace isc::hooks;
using namespace isc::stats;
using namespace std;
namespace {
// Name of the file holding server identifier.
static const char* SERVER_DUID_FILE = "kea-dhcp6-serverid";
}
namespace isc {
namespace dhcp {
......@@ -173,6 +180,24 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
+ string(ex.what())));
}
// Regenerate server identifier if needed.
try {
const std::string duid_file = CfgMgr::instance().getDataDir() + "/" +
std::string(SERVER_DUID_FILE);
DuidPtr duid = CfgMgr::instance().getStagingCfg()->getCfgDUID()->create(duid_file);
server_->serverid_.reset(new Option(Option::V6, D6O_SERVERID, duid->getDuid()));
if (duid) {
LOG_INFO(dhcp6_logger, DHCP6_USING_SERVERID)
.arg(duid->toText())
.arg(duid_file);
}
} catch (const std::exception& ex) {
std::ostringstream err;
err << "unable to configure server identifier: " << ex.what();
return (isc::config::createAnswer(1, err.str()));
}
// Server will start DDNS communications if its enabled.
try {
srv->startD2();
......
......@@ -3,6 +3,44 @@
"module_name": "Dhcp6",
"module_description": "DHCPv6 server daemon",
"config_data": [
{
"item_name": "server-id",
"item_type": "map",
"item_optional": true,
"item_default": { "type": "LLT" },
"map_item_spec": [
{
"item_name": "type",
"item_type": "string",
"item_optional": false,
"item_default": ""
},
{
"item_name": "identifier",
"item_type": "string",
"item_optional": true,
"item_default": ""
},
{
"item_name": "htype",
"item_type": "integer",
"item_optional": true,
"item_default": 0
},
{
"item_name": "time",
"item_type": "integer",
"item_optional": true,
"item_default": 0
},
{
"item_name": "enterprise-id",
"item_type": "integer",
"item_optional": true,
"item_default": 0
}
]
},
{
"item_name": "hooks-libraries",
"item_type": "list",
......
......@@ -644,33 +644,16 @@ etc. The exact reason for rejecting the packet is included in the message.
% DHCP6_RESPONSE_DATA responding with packet type %1 data is %2
A debug message listing the data returned to the client.
% 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 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.
......
......@@ -19,6 +19,7 @@
#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>
......@@ -185,7 +186,7 @@ const std::string Dhcpv6Srv::VENDOR_CLASS_PREFIX("VENDOR_CLASS_");
static const char* SERVER_DUID_FILE = "kea-dhcp6-serverid";
Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
: serverid_(), port_(port), shutdown_(true), alloc_engine_()
: port_(port), serverid_(), shutdown_(true), alloc_engine_()
{
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_START, DHCP6_OPEN_SOCKET).arg(port);
......@@ -201,21 +202,9 @@ Dhcpv6Srv::Dhcpv6Srv(uint16_t port)
}
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()));
// Instantiate allocation engine. The number of allocation attempts equal
// to zero indicates that the allocation engine will use the number of
......@@ -719,38 +708,6 @@ bool Dhcpv6Srv::run() {
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;
......@@ -771,102 +728,6 @@ Dhcpv6Srv::duidToString(const OptionPtr& opt) {
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]);