Commit 2e7c32e7 authored by Tomek Mrugalski's avatar Tomek Mrugalski 🛰
Browse files

[master] Merge branch 'trac3554' (mac-sources parameter added)

parents 4eb8976a c12560be
......@@ -9,6 +9,7 @@ nobase_dist_doc_DATA += examples/kea4/reservations.json
nobase_dist_doc_DATA += examples/kea6/simple.json
nobase_dist_doc_DATA += examples/kea6/several-subnets.json
nobase_dist_doc_DATA += examples/kea6/multiple-options.json
nobase_dist_doc_DATA += examples/kea6/advanced.json
nobase_dist_doc_DATA += examples/ddns/sample1.json
nobase_dist_doc_DATA += examples/ddns/template.json
......
......@@ -4,8 +4,8 @@
{ "Dhcp4":
{
# Kea is told to listen on eth0 interface only.
"interfaces": [ "eth0" ],
# Kea is told to listen on ethX interface only.
"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
......@@ -36,7 +36,7 @@
{
"pools": [ { "pool": "192.0.2.10 - 192.0.2.200" } ],
"subnet": "192.0.2.0/24",
"interface": "eth0",
"interface": "ethX",
"option-data": [
{
"name": "domain-name-servers",
......
......@@ -5,8 +5,8 @@
{ "Dhcp4":
{
# Kea is told to listen on eth0 interface only.
"interfaces": [ "eth0" ],
# Kea is told to listen on ethX interface only.
"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
......
......@@ -5,8 +5,8 @@
{ "Dhcp4":
{
# Kea is told to listen on eth0 interface only.
"interfaces": [ "eth0" ],
# Kea is told to listen on ethX interface only.
"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
......@@ -35,8 +35,8 @@
{
"pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
"subnet": "192.0.2.0/24",
"interface": "eth0"
}
"interface": "ethX"
}
]
},
......
# This is an example configuration file for DHCPv6 server in Kea.
# It attempts to showcase some of the more advanced features.
# Topology wise, it's a basic scenario with one IPv6 subnet configured.
# It is assumed that one subnet (2001:db8:1::/64) is available directly
# over ethX interface.
#
# The following features are currently showcased here:
# 1. Configuration of MAC/hardware address sources in DHCPv6
{ "Dhcp6":
{
# Kea is told to listen on ethX network interface only.
"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"
},
# Kea 0.9.1 introduced MAC/hardware addresses support in DHCPv6. There is
# no single reliable method of getting MAC address information in DHCPv6.
# Kea supports several methods. Depending on your network set up, some
# methods may be more preferable than others, hence the configuration
# parameter. 'mac-sources' is a list of methods. Allowed parameters are:
# any, raw, duid, ipv6-link-local, client-link-addr-option, rfc6939 (which
# is an alias for client-link-addr-option), remote-id, rfc4649 (which is an
# alias for remote-id, subscriber-id, rfc4580 (which is an alias for
# subscriber-id) and docsis.
#
# Note that the order matters. Methods are attempted one by one in the order
# specified until hardware address is obtained. If you don't care which method
# is used, using 'any' is marginally faster than enumerating them all.
#
# If mac-sources are not specified, a default value of 'any' is used.
"mac-sources": [ "client-link-addr-option", "duid", "ipv6-link-local" ],
# 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 repond
# 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": 99,
"severity": "DEBUG"
}
]
}
}
......@@ -4,8 +4,8 @@
{ "Dhcp6":
{
# Kea is told to listen on eth0 interface only.
"interfaces": [ "eth0" ],
# Kea is told to listen on ethX interface only.
"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
......@@ -32,7 +32,7 @@
{
"pools": [ { "pool": "2001:db8:1::/80" } ],
"subnet": "2001:db8:1::/64",
"interface": "eth0",
"interface": "ethX",
"option-data": [
{
"name": "dns-servers",
......
......@@ -5,8 +5,8 @@
{ "Dhcp6":
{
# Kea is told to listen on eth0 interface only.
"interfaces": [ "eth0" ],
# Kea is told to listen on ethX interface only.
"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
......
# This is an example configuration file for DHCPv6 server in Kea.
# It's a basic scenario with four IPv6 subnets configured. It is
# It's a basic scenario with one IPv6 subnet configured. It is
# assumed that one subnet (2001:db8:1::/64 is available directly
# over eth0 interface.
# over ethX interface.
{ "Dhcp6":
{
# Kea is told to listen on eth0 interface only.
"interfaces": [ "eth0" ],
# Kea is told to listen on ethX interface only.
"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
......@@ -33,7 +33,7 @@
{
"pools": [ { "pool": "2001:db8:1::/80" } ],
"subnet": "2001:db8:1::/64",
"interface": "eth0"
"interface": "ethX"
}
]
},
......
......@@ -1838,6 +1838,117 @@ should include options from the isc option space:
</para>
</section>
<section id="mac-in-dhcpv6">
<title>MAC/Hardware addresses in DHCPv6</title>
<para>MAC/hardware addesses are available in DHCPv4 messages
from the clients and administrators
frequently use that information to perform certain tasks, like per host
configuration, address reserveration for specific MAC addresses and other.
Unfortunately, DHCPv6 protocol does not provide any completely reliable way
to retrieve that information. To mitigate that issue a number of mechanisms
have been implemented in Kea that attempt to gather that information. Each
of those mechanisms works in certain cases, but may fail in other cases.
Whether the mechanism works or not in the particular deployment is
somewhat dependent on the network topology and the technologies used.</para>
<para>Kea allows for configuration which of the supported methods should be
used and in which order. This configuration may be considered a fine tuning
of the DHCP deployment. In a typical deployment the default
value of <command>"any"</command> is sufficient and there is no
need to select specific methods. Changing the value of this parameter
is the most useful in cases when an administrator wants to disable
certain method, e.g. if the administrator trusts the network infrastructure
more than the information provided by the clients themselves, the
administrator may prefer information provided by the relays over that
provided by the clients. The format of this parameter is as follows:
<screen>
"Dhcp6": {
<userinput>"mac-sources": [ "method1", "method2", "method3", ... ]</userinput>,
"subnet6": [ ... ],
...
}
</screen>
When not specified, a special value of <emphasis>any</emphasis> is used, which
instructs the server to attempt to use all the methods in sequence and use
value returned by the first one that succeeds.</para>
<para>Supported methods are:
<itemizedlist>
<listitem>
<simpara><command>any</command> - not an actual method, just a keyword that
instructs Kea to try all other methods and use the first one that succeeds.
This is the default operation if no <command>mac-sources</command> are defined.
</simpara>
</listitem>
<listitem>
<simpara><command>raw</command> In principle, a DHCPv6 server could use raw
sockets to receive incoming traffic and extract MAC/hardware address
information. This is currently not implemented and this value has no effect.
</simpara>
</listitem>
<listitem>
<simpara><command>duid</command> - DHCPv6 uses DUID identifiers instead of
MAC addresses. There are currently four DUID types defined, with two of them
(DUID-LLT, which is the default one and DUID-LL) convey MAC address information.
Although RFC3315 forbids it, it is possible to parse those DUIDs and extract
necessary information from them. This method is not completely reliable, as
clients may use other DUID types, namely DUID-EN or DUID-UUID.
</simpara>
</listitem>
<listitem>
<simpara><command>ipv6-link-local</command> - Another possible aquisition
method comes from the source IPv6 address. In typical usage, clients are
sending their packets from IPv6 link-local addresses. There's a good chance
that those addresses are based on EUI-64, which contains MAC address. This
method is not completely reliable, as clients may use other link-local address
types. In particular, privacy extensions, defined in RFC4941, do not use
MAC addresses.
</simpara>
</listitem>
<listitem>
<simpara><command>client-link-addr-option</command> - One extension defined
to alleviate missing MAC issues is client link-layer address option, defined
in <ulink url="http://tools.ietf.org/html/rfc6939">RFC 6939</ulink>. This is
an option that is inserted by a relay and contains information about client's
MAC address. This method requires a relay agent that supports the option and
is configured to insert it. This method is useless for directly connected
clients. This parameter can also be specified as <command>rfc6939</command>,
which is an alias for <command>client-link-addr-option</command>.
</simpara>
</listitem>
<listitem>
<simpara><command>remote-id</command> <ulink
url="http://tools.ietf.org/html/rfc4649">RFC 4649</ulink>
defines remote-id option that is inserted by a relay agent. Depending
on the relay agent configuration, the inserted option may convey client's
MAC address information. This parameter can also be specified as
<command>rfc4649</command>, which is an alias for <command>remote-id</command>.
</simpara>
</listitem>
<listitem>
<simpara><command>subscriber-id</command> - Another option
that is somewhat similar to the previous one is subscriber-id,
defined in <ulink url="http://tools.ietf.org/html/rfc4580">RFC
4580</ulink>. It is, too, inserted by a relay agent that is
configured to insert it. This parameter can also be specified
as <command>rfc4580</command>, which is an alias for
<command>subscriber-id</command>. This method is currently not
implemented.
</simpara>
</listitem>
<listitem>
<simpara><command>docsis</command> - Yet another possible source of MAC
address information are DOCSIS options inserted by a CMTS that acts
as a DHCPv6 relay agent in cable networks. This method is
currently not implemented.
</simpara>
</listitem>
</itemizedlist>
</para>
</section>
<section id="dhcp6-std">
<title>Supported DHCPv6 Standards</title>
......
......@@ -422,6 +422,21 @@
} ]
}
},
{ "item_name": "mac-sources",
"item_type": "list",
"item_optional": true,
"item_default": [ "any" ],
"item_description": "Lists MAC/hardware address acquisition sources",
"list_item_spec":
{
"item_name": "source",
"item_type": "string",
"item_optional": true,
"item_default": "any"
}
} ,
{ "item_name": "dhcp-ddns",
"item_type": "map",
"item_optional": false,
......
......@@ -1184,6 +1184,21 @@ Dhcpv6Srv::createRemovalNameChangeRequest(const Lease6Ptr& lease) {
CfgMgr::instance().getD2ClientMgr().sendRequest(ncr);
}
HWAddrPtr
Dhcpv6Srv::getMAC(const Pkt6Ptr& pkt) {
CfgMACSources mac_sources = CfgMgr::instance().getCurrentCfg()->
getMACSources().get();
HWAddrPtr hwaddr;
for (CfgMACSources::const_iterator it = mac_sources.begin();
it != mac_sources.end(); ++it) {
hwaddr = pkt->getMAC(*it);
if (hwaddr) {
return (hwaddr);
}
}
return (hwaddr);
}
OptionPtr
Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
const Pkt6Ptr& query, const Pkt6Ptr& answer,
......@@ -1253,10 +1268,9 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
hostname = fqdn->getDomainName();
}
// Attempt to get MAC address using any of available mechanisms.
// It's ok if there response is NULL. Hardware address is optional in Lease6
/// @todo: Make this configurable after trac 3554 is done.
HWAddrPtr hwaddr = query->getMAC(Pkt::HWADDR_SOURCE_ANY);
// Attempt to get MAC address using configured mechanisms.
// It's ok if there response is NULL. Hardware address is optional in Lease6.
HWAddrPtr hwaddr = getMAC(query);
// Use allocation engine to pick a lease for this client. Allocation engine
// will try to honour the hint, but it is just a hint - some other address
......@@ -1376,8 +1390,7 @@ Dhcpv6Srv::assignIA_PD(const Subnet6Ptr& subnet, const DuidPtr& duid,
// Attempt to get MAC address using any of available mechanisms.
// It's ok if there response is NULL. Hardware address is optional in Lease6
/// @todo: Make this configurable after trac 3554 is done.
HWAddrPtr hwaddr = query->getMAC(Pkt::HWADDR_SOURCE_ANY);
HWAddrPtr hwaddr = getMAC(query);
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_PD_REQUEST)
.arg(duid ? duid->toText() : "(no-duid)").arg(ia->getIAID())
......@@ -1569,6 +1582,9 @@ Dhcpv6Srv::extendIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
lease->fqdn_fwd_ = do_fwd;
lease->fqdn_rev_ = do_rev;
/// @todo: check if hardware address has changed since last update.
/// And modify lease->hwaddr_ if it did.
ia_rsp->setT1(subnet->getT1());
ia_rsp->setT2(subnet->getT2());
......
......@@ -596,6 +596,14 @@ protected:
/// @param pkt packet to be classified
void classifyPacket(const Pkt6Ptr& pkt);
/// @brief Attempts to get a MAC/hardware address using configred sources
///
/// Tries to extract MAC/hardware address information from the packet
/// using MAC sources configured in 'mac-sources' configuration parameter.
///
/// @param pkt will try to exact MAC address from this packet
/// @return HWaddr pointer (or NULL if configured methods fail)
static HWAddrPtr getMAC(const Pkt6Ptr& pkt);
/// @brief this is a prefix added to the contend of vendor-class option
///
......
......@@ -613,6 +613,9 @@ namespace dhcp {
parser = new HooksLibrariesParser(config_id);
} else if (config_id.compare("dhcp-ddns") == 0) {
parser = new D2ClientConfigParser(config_id);
} else if (config_id.compare("mac-sources") == 0) {
parser = new MACSourcesListConfigParser(config_id,
globalContext());
} else {
isc_throw(DhcpConfigError,
"unsupported global configuration parameter: "
......
......@@ -15,9 +15,6 @@
#ifndef DHCP6_CONFIG_PARSER_H
#define DHCP6_CONFIG_PARSER_H
/// @todo: This header file and its .cc counterpart are very similar between
/// DHCPv4 and DHCPv6. They should be merged. See ticket #2355.
#include <cc/data.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <exceptions/exceptions.h>
......
......@@ -3627,4 +3627,79 @@ TEST_F(Dhcp6ParserTest, reservationBogus) {
}
/// The goal of this test is to verify that configuration can include
/// MAC/Hardware sources. This test also checks if the aliases are
/// handled properly (rfc6939 = client-addr-relay, rfc4649 = remote-id,
/// rfc4580 = subscriber-id).
TEST_F(Dhcp6ParserTest, macSources) {
ConstElementPtr status;
EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
Element::fromJSON("{ \"interfaces\": [ \"*\" ],"
"\"mac-sources\": [ \"rfc6939\", \"rfc4649\", \"rfc4580\","
"\"client-link-addr-option\", \"remote-id\", \"subscriber-id\"],"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ ], "
"\"valid-lifetime\": 4000 }")));
// returned value should be 0 (success)
checkResult(status, 0);
CfgMACSources mac_sources = CfgMgr::instance().getStagingCfg()->getMACSources().get();
ASSERT_EQ(6, mac_sources.size());
// Let's check the aliases. They should be recognized to their base methods.
EXPECT_EQ(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION, mac_sources[0]);
EXPECT_EQ(HWAddr::HWADDR_SOURCE_REMOTE_ID, mac_sources[1]);
EXPECT_EQ(HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID, mac_sources[2]);
// Let's check if the actual methods are recognized properly.
EXPECT_EQ(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION, mac_sources[3]);
EXPECT_EQ(HWAddr::HWADDR_SOURCE_REMOTE_ID, mac_sources[4]);
EXPECT_EQ(HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID, mac_sources[5]);
}
/// The goal of this test is to verify that MAC sources configuration can be
/// empty.
TEST_F(Dhcp6ParserTest, macSourcesEmpty) {
ConstElementPtr status;
EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
Element::fromJSON("{ \"interfaces\": [ \"*\" ],"
"\"mac-sources\": [ ],"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ ], "
"\"valid-lifetime\": 4000 }")));
// returned value should be 0 (success)
checkResult(status, 0);
CfgMACSources mac_sources = CfgMgr::instance().getStagingCfg()->getMACSources().get();
EXPECT_EQ(0, mac_sources.size());
}
/// The goal of this test is to verify that MAC sources configuration can
/// only use valid parameters.
TEST_F(Dhcp6ParserTest, macSourcesBogus) {
ConstElementPtr status;
EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
Element::fromJSON("{ \"interfaces\": [ \"*\" ],"
"\"mac-sources\": [ \"from-wire\" ],"
"\"preferred-lifetime\": 3000,"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, "
"\"subnet6\": [ ], "
"\"valid-lifetime\": 4000 }")));
// returned value should be 1 (failure)
checkResult(status, 1);
}
};
......@@ -63,7 +63,7 @@ Dhcp6Client::applyRcvdConfiguration(const Pkt6Ptr& reply) {
Opts opts = reply->options_;
// Let's try to get a MAC
HWAddrPtr hwaddr = reply->getMAC(Pkt::HWADDR_SOURCE_ANY);
HWAddrPtr hwaddr = reply->getMAC(HWAddr::HWADDR_SOURCE_ANY);
// Set the global status code to default: success and not received.
config_.resetGlobalStatusCode();
......
......@@ -27,6 +27,16 @@
namespace isc {
namespace dhcp {
const uint32_t HWAddr::HWADDR_SOURCE_ANY = 0xffffffff;
const uint32_t HWAddr::HWADDR_SOURCE_UNKNOWN = 0x00000000;
const uint32_t HWAddr::HWADDR_SOURCE_RAW = 0x00000001;
const uint32_t HWAddr::HWADDR_SOURCE_DUID = 0x00000002;
const uint32_t HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL = 0x00000004;
const uint32_t HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION = 0x00000008;
const uint32_t HWAddr::HWADDR_SOURCE_REMOTE_ID = 0x00000010;
const uint32_t HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID = 0x00000020;
const uint32_t HWAddr::HWADDR_SOURCE_DOCSIS = 0x00000040;
HWAddr::HWAddr()
:htype_(HTYPE_ETHER), source_(0) {
}
......
......@@ -34,6 +34,53 @@ public:
/// @brief Maximum size of a hardware address.
static const size_t MAX_HWADDR_LEN = 20;
/// @defgroup hw_sources Specifies where a given MAC/hardware address was
/// obtained.
///
/// @brief The list covers all possible MAC/hw address sources.
///
/// @{
/// Not really a type, only used in getMAC() calls.
static const uint32_t HWADDR_SOURCE_ANY;
/// Used when actual origin is not known, e.g. when reading from a
/// lease database that didn't store that information.
static const uint32_t HWADDR_SOURCE_UNKNOWN;
/// Obtained first hand from raw socket (100% reliable).
static const uint32_t HWADDR_SOURCE_RAW;
/// Extracted from DUID-LL or DUID-LLT (not 100% reliable as the client
/// can send fake DUID).
static const uint32_t HWADDR_SOURCE_DUID;
/// Extracted from IPv6 link-local address. Not 100% reliable, as the
/// client can use different IID other than EUI-64, e.g. Windows supports
/// RFC4941 and uses random values instead of EUI-64.
static const uint32_t HWADDR_SOURCE_IPV6_LINK_LOCAL;
/// Get it from RFC6939 option. (A relay agent can insert client link layer
/// address option). Note that a skilled attacker can fake that by sending
/// his request relayed, so the legitimate relay will think it's a second
/// relay.
static const uint32_t HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION;
/// A relay can insert remote-id. In some deployments it contains a MAC
/// address (RFC4649).
static const uint32_t HWADDR_SOURCE_REMOTE_ID;
/// A relay can insert a subscriber-id option. In some deployments it
/// contains a MAC address (RFC4580).
static const uint32_t HWADDR_SOURCE_SUBSCRIBER_ID;
/// A CMTS (acting as DHCP relay agent) that supports DOCSIS standard
/// can insert DOCSIS options that contain client's MAC address.
/// Client in this context would be a cable modem.
static const uint32_t HWADDR_SOURCE_DOCSIS;