2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-29 21:18:02 +00:00

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

This commit is contained in:
Tomek Mrugalski 2014-12-23 16:43:43 +01:00
commit 2e7c32e7c1
32 changed files with 783 additions and 123 deletions

View File

@ -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/simple.json
nobase_dist_doc_DATA += examples/kea6/several-subnets.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/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/sample1.json
nobase_dist_doc_DATA += examples/ddns/template.json nobase_dist_doc_DATA += examples/ddns/template.json

View File

@ -4,8 +4,8 @@
{ "Dhcp4": { "Dhcp4":
{ {
# Kea is told to listen on eth0 interface only. # Kea is told to listen on ethX interface only.
"interfaces": [ "eth0" ], "interfaces": [ "ethX" ],
# We need to specify lease type. As of May 2014, three backends are supported: # 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 # 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" } ], "pools": [ { "pool": "192.0.2.10 - 192.0.2.200" } ],
"subnet": "192.0.2.0/24", "subnet": "192.0.2.0/24",
"interface": "eth0", "interface": "ethX",
"option-data": [ "option-data": [
{ {
"name": "domain-name-servers", "name": "domain-name-servers",

View File

@ -5,8 +5,8 @@
{ "Dhcp4": { "Dhcp4":
{ {
# Kea is told to listen on eth0 interface only. # Kea is told to listen on ethX interface only.
"interfaces": [ "eth0" ], "interfaces": [ "ethX" ],
# We need to specify lease type. As of May 2014, three backends are supported: # 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 # memfile, mysql and pgsql. We'll just use memfile, because it doesn't require

View File

@ -5,8 +5,8 @@
{ "Dhcp4": { "Dhcp4":
{ {
# Kea is told to listen on eth0 interface only. # Kea is told to listen on ethX interface only.
"interfaces": [ "eth0" ], "interfaces": [ "ethX" ],
# We need to specify lease type. As of May 2014, three backends are supported: # 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 # memfile, mysql and pgsql. We'll just use memfile, because it doesn't require
@ -35,7 +35,7 @@
{ {
"pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ], "pools": [ { "pool": "192.0.2.1 - 192.0.2.200" } ],
"subnet": "192.0.2.0/24", "subnet": "192.0.2.0/24",
"interface": "eth0" "interface": "ethX"
} }
] ]
}, },

View File

@ -0,0 +1,79 @@
# 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"
}
]
}
}

View File

@ -4,8 +4,8 @@
{ "Dhcp6": { "Dhcp6":
{ {
# Kea is told to listen on eth0 interface only. # Kea is told to listen on ethX interface only.
"interfaces": [ "eth0" ], "interfaces": [ "ethX" ],
# We need to specify lease type. As of May 2014, three backends are supported: # 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 # memfile, mysql and pgsql. We'll just use memfile, because it doesn't require
@ -32,7 +32,7 @@
{ {
"pools": [ { "pool": "2001:db8:1::/80" } ], "pools": [ { "pool": "2001:db8:1::/80" } ],
"subnet": "2001:db8:1::/64", "subnet": "2001:db8:1::/64",
"interface": "eth0", "interface": "ethX",
"option-data": [ "option-data": [
{ {
"name": "dns-servers", "name": "dns-servers",

View File

@ -5,8 +5,8 @@
{ "Dhcp6": { "Dhcp6":
{ {
# Kea is told to listen on eth0 interface only. # Kea is told to listen on ethX interface only.
"interfaces": [ "eth0" ], "interfaces": [ "ethX" ],
# We need to specify lease type. As of May 2014, three backends are supported: # 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 # memfile, mysql and pgsql. We'll just use memfile, because it doesn't require

View File

@ -1,13 +1,13 @@
# This is an example configuration file for DHCPv6 server in Kea. # 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 # assumed that one subnet (2001:db8:1::/64 is available directly
# over eth0 interface. # over ethX interface.
{ "Dhcp6": { "Dhcp6":
{ {
# Kea is told to listen on eth0 interface only. # Kea is told to listen on ethX interface only.
"interfaces": [ "eth0" ], "interfaces": [ "ethX" ],
# We need to specify lease type. As of May 2014, three backends are supported: # 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 # memfile, mysql and pgsql. We'll just use memfile, because it doesn't require
@ -33,7 +33,7 @@
{ {
"pools": [ { "pool": "2001:db8:1::/80" } ], "pools": [ { "pool": "2001:db8:1::/80" } ],
"subnet": "2001:db8:1::/64", "subnet": "2001:db8:1::/64",
"interface": "eth0" "interface": "ethX"
} }
] ]
}, },

View File

@ -1838,6 +1838,117 @@ should include options from the isc option space:
</para> </para>
</section> </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"> <section id="dhcp6-std">
<title>Supported DHCPv6 Standards</title> <title>Supported DHCPv6 Standards</title>

View File

@ -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_name": "dhcp-ddns",
"item_type": "map", "item_type": "map",
"item_optional": false, "item_optional": false,

View File

@ -1184,6 +1184,21 @@ Dhcpv6Srv::createRemovalNameChangeRequest(const Lease6Ptr& lease) {
CfgMgr::instance().getD2ClientMgr().sendRequest(ncr); 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 OptionPtr
Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid, Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
const Pkt6Ptr& query, const Pkt6Ptr& answer, const Pkt6Ptr& query, const Pkt6Ptr& answer,
@ -1253,10 +1268,9 @@ Dhcpv6Srv::assignIA_NA(const Subnet6Ptr& subnet, const DuidPtr& duid,
hostname = fqdn->getDomainName(); hostname = fqdn->getDomainName();
} }
// Attempt to get MAC address using any of available mechanisms. // Attempt to get MAC address using configured mechanisms.
// It's ok if there response is NULL. Hardware address is optional in Lease6 // 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 = getMAC(query);
HWAddrPtr hwaddr = query->getMAC(Pkt::HWADDR_SOURCE_ANY);
// Use allocation engine to pick a lease for this client. Allocation engine // 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 // 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. // Attempt to get MAC address using any of available mechanisms.
// It's ok if there response is NULL. Hardware address is optional in Lease6 // 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 = getMAC(query);
HWAddrPtr hwaddr = query->getMAC(Pkt::HWADDR_SOURCE_ANY);
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_PD_REQUEST) LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PROCESS_IA_PD_REQUEST)
.arg(duid ? duid->toText() : "(no-duid)").arg(ia->getIAID()) .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_fwd_ = do_fwd;
lease->fqdn_rev_ = do_rev; 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->setT1(subnet->getT1());
ia_rsp->setT2(subnet->getT2()); ia_rsp->setT2(subnet->getT2());

View File

@ -596,6 +596,14 @@ protected:
/// @param pkt packet to be classified /// @param pkt packet to be classified
void classifyPacket(const Pkt6Ptr& pkt); 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 /// @brief this is a prefix added to the contend of vendor-class option
/// ///

View File

@ -613,6 +613,9 @@ namespace dhcp {
parser = new HooksLibrariesParser(config_id); parser = new HooksLibrariesParser(config_id);
} else if (config_id.compare("dhcp-ddns") == 0) { } else if (config_id.compare("dhcp-ddns") == 0) {
parser = new D2ClientConfigParser(config_id); parser = new D2ClientConfigParser(config_id);
} else if (config_id.compare("mac-sources") == 0) {
parser = new MACSourcesListConfigParser(config_id,
globalContext());
} else { } else {
isc_throw(DhcpConfigError, isc_throw(DhcpConfigError,
"unsupported global configuration parameter: " "unsupported global configuration parameter: "

View File

@ -15,9 +15,6 @@
#ifndef DHCP6_CONFIG_PARSER_H #ifndef DHCP6_CONFIG_PARSER_H
#define 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 <cc/data.h>
#include <dhcpsrv/parsers/dhcp_parsers.h> #include <dhcpsrv/parsers/dhcp_parsers.h>
#include <exceptions/exceptions.h> #include <exceptions/exceptions.h>

View File

@ -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);
}
}; };

View File

@ -63,7 +63,7 @@ Dhcp6Client::applyRcvdConfiguration(const Pkt6Ptr& reply) {
Opts opts = reply->options_; Opts opts = reply->options_;
// Let's try to get a MAC // 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. // Set the global status code to default: success and not received.
config_.resetGlobalStatusCode(); config_.resetGlobalStatusCode();

View File

@ -27,6 +27,16 @@
namespace isc { namespace isc {
namespace dhcp { 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() HWAddr::HWAddr()
:htype_(HTYPE_ETHER), source_(0) { :htype_(HTYPE_ETHER), source_(0) {
} }

View File

@ -34,6 +34,53 @@ public:
/// @brief Maximum size of a hardware address. /// @brief Maximum size of a hardware address.
static const size_t MAX_HWADDR_LEN = 20; 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;
/// @}
/// @brief default constructor /// @brief default constructor
HWAddr(); HWAddr();

View File

@ -15,6 +15,7 @@
#include <utility> #include <utility>
#include <dhcp/pkt.h> #include <dhcp/pkt.h>
#include <dhcp/iface_mgr.h> #include <dhcp/iface_mgr.h>
#include <dhcp/hwaddr.h>
#include <vector> #include <vector>
namespace isc { namespace isc {
@ -129,11 +130,11 @@ Pkt::getMAC(uint32_t hw_addr_src) {
HWAddrPtr mac; HWAddrPtr mac;
// Method 1: from raw sockets. // Method 1: from raw sockets.
if (hw_addr_src & HWADDR_SOURCE_RAW) { if (hw_addr_src & HWAddr::HWADDR_SOURCE_RAW) {
mac = getRemoteHWAddr(); mac = getRemoteHWAddr();
if (mac) { if (mac) {
return (mac); return (mac);
} else if (hw_addr_src == HWADDR_SOURCE_RAW) { } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_RAW) {
// If we're interested only in RAW sockets as source of that info, // If we're interested only in RAW sockets as source of that info,
// there's no point in trying other options. // there's no point in trying other options.
return (HWAddrPtr()); return (HWAddrPtr());
@ -143,11 +144,11 @@ Pkt::getMAC(uint32_t hw_addr_src) {
// Method 2: Extracted from DUID-LLT or DUID-LL // Method 2: Extracted from DUID-LLT or DUID-LL
// Method 3: Extracted from source IPv6 link-local address // Method 3: Extracted from source IPv6 link-local address
if (hw_addr_src & HWADDR_SOURCE_IPV6_LINK_LOCAL) { if (hw_addr_src & HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL) {
mac = getMACFromSrcLinkLocalAddr(); mac = getMACFromSrcLinkLocalAddr();
if (mac) { if (mac) {
return (mac); return (mac);
} else if (hw_addr_src == HWADDR_SOURCE_IPV6_LINK_LOCAL) { } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL) {
// If we're interested only in link-local addr as source of that // If we're interested only in link-local addr as source of that
// info, there's no point in trying other options. // info, there's no point in trying other options.
return (HWAddrPtr()); return (HWAddrPtr());
@ -155,11 +156,11 @@ Pkt::getMAC(uint32_t hw_addr_src) {
} }
// Method 4: From client link-layer address option inserted by a relay // Method 4: From client link-layer address option inserted by a relay
if (hw_addr_src & HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION) { if (hw_addr_src & HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION) {
mac = getMACFromIPv6RelayOpt(); mac = getMACFromIPv6RelayOpt();
if (mac) { if (mac) {
return (mac); return (mac);
} else if (hw_addr_src == HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION) { } else if (hw_addr_src == HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION) {
// If we're interested only in RFC6939 link layer address as source // If we're interested only in RFC6939 link layer address as source
// of that info, there's no point in trying other options. // of that info, there's no point in trying other options.
return (HWAddrPtr()); return (HWAddrPtr());
@ -223,6 +224,5 @@ Pkt::getMACFromIPv6(const isc::asiolink::IOAddress& addr) {
return (HWAddrPtr(new HWAddr(bin, hwtype))); return (HWAddrPtr(new HWAddr(bin, hwtype)));
} }
}; };
}; };

View File

@ -37,58 +37,6 @@ namespace dhcp {
/// @note This is abstract class. Please instantiate derived classes /// @note This is abstract class. Please instantiate derived classes
/// such as @c Pkt4 or @c Pkt6. /// such as @c Pkt4 or @c Pkt6.
class Pkt { class Pkt {
public:
/// @defgroup hw_sources Specifies where a given MAC/hardware address was
/// obtained.
///
/// @brief The list covers all possible MAC/hw address sources.
///
/// @note The uncommented ones are currently supported. When you implement
/// a new method, please uncomment appropriate line here.
///
/// @{
/// Not really a type, only used in getMAC() calls.
static const uint32_t HWADDR_SOURCE_ANY = 0xffff;
/// 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 = 0x0000;
/// Obtained first hand from raw socket (100% reliable).
static const uint32_t HWADDR_SOURCE_RAW = 0x0001;
/// Extracted from DUID-LL or DUID-LLT (not 100% reliable as the client
/// can send fake DUID).
//static const uint32_t HWADDR_SOURCE_DUID = 0x0002;
/// 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 = 0x0004;
/// 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 = 0x0008;
/// A relay can insert remote-id. In some deployments it contains a MAC
/// address (RFC4649).
//static const uint32_t HWADDR_SOURCE_REMOTE_ID = 0x0010;
/// 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 = 0x0020;
/// 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_OPTIONS = 0x0040;
/// @}
protected: protected:
/// @brief Constructor. /// @brief Constructor.

View File

@ -857,8 +857,8 @@ TEST_F(Pkt4Test, getMAC) {
Pkt4 pkt(DHCPOFFER, 1234); Pkt4 pkt(DHCPOFFER, 1234);
// DHCPv4 packet by default doens't have MAC address specified. // DHCPv4 packet by default doens't have MAC address specified.
EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_ANY)); EXPECT_FALSE(pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY));
EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_RAW)); EXPECT_FALSE(pkt.getMAC(HWAddr::HWADDR_SOURCE_RAW));
// Let's invent a MAC // Let's invent a MAC
const uint8_t hw[] = { 2, 4, 6, 8, 10, 12 }; // MAC const uint8_t hw[] = { 2, 4, 6, 8, 10, 12 }; // MAC
@ -869,12 +869,12 @@ TEST_F(Pkt4Test, getMAC) {
pkt.setRemoteHWAddr(dummy_hwaddr); pkt.setRemoteHWAddr(dummy_hwaddr);
// Now we should be able to get something // Now we should be able to get something
ASSERT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_ANY)); ASSERT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY));
ASSERT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_RAW)); ASSERT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_RAW));
// Check that the returned MAC is indeed the expected one // Check that the returned MAC is indeed the expected one
ASSERT_TRUE(*dummy_hwaddr == *pkt.getMAC(Pkt::HWADDR_SOURCE_ANY)); ASSERT_TRUE(*dummy_hwaddr == *pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY));
ASSERT_TRUE(*dummy_hwaddr == *pkt.getMAC(Pkt::HWADDR_SOURCE_RAW)); ASSERT_TRUE(*dummy_hwaddr == *pkt.getMAC(HWAddr::HWADDR_SOURCE_RAW));
} }
} // end of anonymous namespace } // end of anonymous namespace

View File

@ -883,20 +883,20 @@ TEST_F(Pkt6Test, getMAC) {
Pkt6 pkt(DHCPV6_ADVERTISE, 1234); Pkt6 pkt(DHCPV6_ADVERTISE, 1234);
// DHCPv6 packet by default doens't have MAC address specified. // DHCPv6 packet by default doens't have MAC address specified.
EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_ANY)); EXPECT_FALSE(pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY));
EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_RAW)); EXPECT_FALSE(pkt.getMAC(HWAddr::HWADDR_SOURCE_RAW));
// We haven't specified source IPv6 address, so this method should // We haven't specified source IPv6 address, so this method should
// fail, too // fail, too
EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL)); EXPECT_FALSE(pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL));
// Let's check if setting IPv6 address improves the situation. // Let's check if setting IPv6 address improves the situation.
IOAddress linklocal_eui64("fe80::204:06ff:fe08:0a0c"); IOAddress linklocal_eui64("fe80::204:06ff:fe08:0a0c");
pkt.setRemoteAddr(linklocal_eui64); pkt.setRemoteAddr(linklocal_eui64);
EXPECT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_ANY)); EXPECT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY));
EXPECT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL)); EXPECT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL));
EXPECT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL | EXPECT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL |
Pkt::HWADDR_SOURCE_RAW)); HWAddr::HWADDR_SOURCE_RAW));
pkt.setRemoteAddr(IOAddress("::")); pkt.setRemoteAddr(IOAddress("::"));
// Let's invent a MAC // Let's invent a MAC
@ -908,14 +908,14 @@ TEST_F(Pkt6Test, getMAC) {
pkt.setRemoteHWAddr(dummy_hwaddr); pkt.setRemoteHWAddr(dummy_hwaddr);
// Now we should be able to get something // Now we should be able to get something
ASSERT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_ANY)); ASSERT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY));
ASSERT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_RAW)); ASSERT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_RAW));
EXPECT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL | EXPECT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL |
Pkt::HWADDR_SOURCE_RAW)); HWAddr::HWADDR_SOURCE_RAW));
// Check that the returned MAC is indeed the expected one // Check that the returned MAC is indeed the expected one
ASSERT_TRUE(*dummy_hwaddr == *pkt.getMAC(Pkt::HWADDR_SOURCE_ANY)); ASSERT_TRUE(*dummy_hwaddr == *pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY));
ASSERT_TRUE(*dummy_hwaddr == *pkt.getMAC(Pkt::HWADDR_SOURCE_RAW)); ASSERT_TRUE(*dummy_hwaddr == *pkt.getMAC(HWAddr::HWADDR_SOURCE_RAW));
} }
// Test checks whether getMACFromIPv6LinkLocal() returns the hardware (MAC) // Test checks whether getMACFromIPv6LinkLocal() returns the hardware (MAC)
@ -941,11 +941,11 @@ TEST_F(Pkt6Test, getMACFromIPv6LinkLocal_direct) {
// If received from a global address, this method should fail // If received from a global address, this method should fail
pkt.setRemoteAddr(global); pkt.setRemoteAddr(global);
EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL)); EXPECT_FALSE(pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL));
// If received from link-local that is EUI-64 based, it should succeed // If received from link-local that is EUI-64 based, it should succeed
pkt.setRemoteAddr(linklocal_eui64); pkt.setRemoteAddr(linklocal_eui64);
HWAddrPtr found = pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL); HWAddrPtr found = pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL);
ASSERT_TRUE(found); ASSERT_TRUE(found);
stringstream tmp; stringstream tmp;
@ -980,15 +980,15 @@ TEST_F(Pkt6Test, getMACFromIPv6LinkLocal_singleRelay) {
// If received from a global address, this method should fail // If received from a global address, this method should fail
pkt.relay_info_[0].peeraddr_ = global; pkt.relay_info_[0].peeraddr_ = global;
EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL)); EXPECT_FALSE(pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL));
// If received from a link-local that does not use EUI-64, it should fail // If received from a link-local that does not use EUI-64, it should fail
pkt.relay_info_[0].peeraddr_ = linklocal_noneui64; pkt.relay_info_[0].peeraddr_ = linklocal_noneui64;
EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL)); EXPECT_FALSE(pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL));
// If received from link-local that is EUI-64 based, it should succeed // If received from link-local that is EUI-64 based, it should succeed
pkt.relay_info_[0].peeraddr_ = linklocal_eui64; pkt.relay_info_[0].peeraddr_ = linklocal_eui64;
HWAddrPtr found = pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL); HWAddrPtr found = pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL);
ASSERT_TRUE(found); ASSERT_TRUE(found);
stringstream tmp; stringstream tmp;
@ -1041,7 +1041,7 @@ TEST_F(Pkt6Test, getMACFromIPv6LinkLocal_multiRelay) {
pkt.setIndex(iface->getIndex()); pkt.setIndex(iface->getIndex());
// The method should return MAC based on the first relay that was closest // The method should return MAC based on the first relay that was closest
HWAddrPtr found = pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL); HWAddrPtr found = pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL);
ASSERT_TRUE(found); ASSERT_TRUE(found);
// Let's check the info now. // Let's check the info now.
@ -1058,7 +1058,7 @@ TEST_F(Pkt6Test, getMACFromIPv6RelayOpt_singleRelay) {
Pkt6 pkt(DHCPV6_SOLICIT, 1234); Pkt6 pkt(DHCPV6_SOLICIT, 1234);
// Packets that are not relayed should fail // Packets that are not relayed should fail
EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION)); EXPECT_FALSE(pkt.getMAC(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION));
// Now pretend it was relayed by a single relay. // Now pretend it was relayed by a single relay.
Pkt6::RelayInfo info; Pkt6::RelayInfo info;
@ -1075,7 +1075,7 @@ TEST_F(Pkt6Test, getMACFromIPv6RelayOpt_singleRelay) {
pkt.addRelayInfo(info); pkt.addRelayInfo(info);
ASSERT_EQ(1, pkt.relay_info_.size()); ASSERT_EQ(1, pkt.relay_info_.size());
HWAddrPtr found = pkt.getMAC(Pkt::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION); HWAddrPtr found = pkt.getMAC(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION);
ASSERT_TRUE(found); ASSERT_TRUE(found);
stringstream tmp; stringstream tmp;
@ -1108,7 +1108,7 @@ TEST_F(Pkt6Test, getMACFromIPv6RelayOpt_multipleRelay) {
pkt.addRelayInfo(info2); pkt.addRelayInfo(info2);
ASSERT_EQ(2, pkt.relay_info_.size()); ASSERT_EQ(2, pkt.relay_info_.size());
EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION)); EXPECT_FALSE(pkt.getMAC(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION));
// Let's envolve the packet with a third relay (now the closest to the client) // Let's envolve the packet with a third relay (now the closest to the client)
// that inserts the correct client_linklayer_addr option. // that inserts the correct client_linklayer_addr option.
@ -1123,11 +1123,12 @@ TEST_F(Pkt6Test, getMACFromIPv6RelayOpt_multipleRelay) {
ASSERT_EQ(3, pkt.relay_info_.size()); ASSERT_EQ(3, pkt.relay_info_.size());
// Now extract the MAC address from the relayed option // Now extract the MAC address from the relayed option
HWAddrPtr found = pkt.getMAC(Pkt::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION); HWAddrPtr found = pkt.getMAC(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION);
ASSERT_TRUE(found); ASSERT_TRUE(found);
stringstream tmp; stringstream tmp;
tmp << "hwtype=1 fa:30:0b:fa:c0:fe"; tmp << "hwtype=1 fa:30:0b:fa:c0:fe";
EXPECT_EQ(tmp.str(), found->toText(true)); EXPECT_EQ(tmp.str(), found->toText(true));
} }
} }

View File

@ -69,6 +69,7 @@ libkea_dhcpsrv_la_SOURCES += cfg_option.cc cfg_option.h
libkea_dhcpsrv_la_SOURCES += cfg_option_def.cc cfg_option_def.h libkea_dhcpsrv_la_SOURCES += cfg_option_def.cc cfg_option_def.h
libkea_dhcpsrv_la_SOURCES += cfg_subnets4.cc cfg_subnets4.h libkea_dhcpsrv_la_SOURCES += cfg_subnets4.cc cfg_subnets4.h
libkea_dhcpsrv_la_SOURCES += cfg_subnets6.cc cfg_subnets6.h libkea_dhcpsrv_la_SOURCES += cfg_subnets6.cc cfg_subnets6.h
libkea_dhcpsrv_la_SOURCES += cfg_mac_source.cc cfg_mac_source.h
libkea_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h libkea_dhcpsrv_la_SOURCES += cfgmgr.cc cfgmgr.h
libkea_dhcpsrv_la_SOURCES += csv_lease_file4.cc csv_lease_file4.h libkea_dhcpsrv_la_SOURCES += csv_lease_file4.cc csv_lease_file4.h
libkea_dhcpsrv_la_SOURCES += csv_lease_file6.cc csv_lease_file6.h libkea_dhcpsrv_la_SOURCES += csv_lease_file6.cc csv_lease_file6.h

View File

@ -0,0 +1,59 @@
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <config.h>
#include <dhcpsrv/cfg_mac_source.h>
#include <exceptions/exceptions.h>
#include <dhcp/hwaddr.h>
namespace isc {
namespace dhcp {
CfgMACSource::CfgMACSource() {
// By default, use any hardware source that is available.
mac_sources_.push_back(HWAddr::HWADDR_SOURCE_ANY);
}
uint32_t CfgMACSource::MACSourceFromText(const std::string& name) {
struct {
const char * name;
uint32_t type;
} sources[] = {
{ "any", HWAddr::HWADDR_SOURCE_ANY },
{ "raw", HWAddr::HWADDR_SOURCE_RAW },
{ "duid", HWAddr::HWADDR_SOURCE_DUID },
{ "ipv6-link-local", HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL },
{ "client-link-addr-option", HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION },
{ "rfc6939", HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION },
{ "remote-id", HWAddr::HWADDR_SOURCE_REMOTE_ID },
{ "rfc4649", HWAddr::HWADDR_SOURCE_REMOTE_ID },
{ "subscriber-id", HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID },
{ "rfc4580", HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID },
{ "docsis", HWAddr::HWADDR_SOURCE_DOCSIS }
};
for (int i=0; i < sizeof(sources)/sizeof(sources[0]); ++i) {
if (name.compare(sources[i].name) == 0) {
return (sources[i].type);
}
}
isc_throw(BadValue, "Can't convert '" << name << "' to any known MAC source.");
}
};
};

View File

@ -0,0 +1,88 @@
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#ifndef CFG_MAC_SOURCE_H
#define CFG_MAC_SOURCE_H
#include <vector>
namespace isc {
namespace dhcp {
/// @brief Container for defined MAC/hardware address sources
typedef std::vector<uint32_t> CfgMACSources;
/// @brief Wrapper class that holds MAC/hardware address sources
///
/// It's a simple wrapper around a vector of uint32_t, with each entry
/// holding one MAC source.
class CfgMACSource {
public:
/// @brief Default constructor.
///
/// Sets source to 'any'.
CfgMACSource();
/// @brief Attempts to convert known hardware address sources to uint32_t
///
/// Supported strings are: \li any => 0xffffffff
/// \li raw => 0x00000001
/// \li duid => 0x00000002
/// \li ipv6-link-local 0x00000004
/// \li client-link-addr-option, rfc6939 => 0x00000008
/// \li remote-id, rfc4649 => 0x00000010
/// \li subscriber-id, rfc4580 => 0x00000020
/// \li docsis => 0x00000040
///
/// For specific constants, see @ref isc::dhcp::HWAddr class.
///
/// @throw BadValue if specified string is unknown
/// @return bitmask version of a given method
static uint32_t MACSourceFromText(const std::string& name);
/// @brief Adds additional MAC/hardware address aquisition.
///
/// @param source MAC source (see constants in Pkt::HWADDR_SOURCE_*)
///
/// Specified source is being added to the mac_sources_ array.
/// @todo implement add(string) version of this method.
void add(uint32_t source) {
mac_sources_.push_back(source);
}
/// @brief Provides access to the configure MAC/Hardware address sources.
///
/// @note The const reference returned is only valid as long as the
/// object that returned it.
const CfgMACSources& get() const {
return mac_sources_;
}
/// @brief Removes any configured MAC/Hardware address sources.
void clear() {
mac_sources_.clear();
}
protected:
/// @brief Actual MAC sources storage
CfgMACSources mac_sources_;
};
};
};
#endif

View File

@ -17,6 +17,7 @@
#include <dhcpsrv/cfgmgr.h> #include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/cfg_option.h> #include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/parsers/dhcp_parsers.h> #include <dhcpsrv/parsers/dhcp_parsers.h>
#include <dhcpsrv/cfg_mac_source.h>
#include <hooks/hooks_manager.h> #include <hooks/hooks_manager.h>
#include <util/encode/hex.h> #include <util/encode/hex.h>
#include <util/strutil.h> #include <util/strutil.h>
@ -187,6 +188,7 @@ InterfaceListConfigParser(const std::string& param_name,
void void
InterfaceListConfigParser::build(ConstElementPtr value) { InterfaceListConfigParser::build(ConstElementPtr value) {
CfgIface cfg_iface; CfgIface cfg_iface;
BOOST_FOREACH(ConstElementPtr iface, value->listValue()) { BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
std::string iface_name = iface->stringValue(); std::string iface_name = iface->stringValue();
try { try {
@ -206,6 +208,45 @@ InterfaceListConfigParser::commit() {
// Nothing to do. // Nothing to do.
} }
// ******************** MACSourcesListConfigParser *************************
MACSourcesListConfigParser::
MACSourcesListConfigParser(const std::string& param_name,
ParserContextPtr global_context)
: param_name_(param_name), global_context_(global_context) {
if (param_name_ != "mac-sources") {
isc_throw(BadValue, "Internal error. MAC sources configuration "
"parser called for the wrong parameter: " << param_name);
}
}
void
MACSourcesListConfigParser::build(ConstElementPtr value) {
CfgIface cfg_iface;
uint32_t source = 0;
// By default, there's only one source defined: ANY.
// If user specified anything, we need to get rid of that default.
CfgMgr::instance().getStagingCfg()->getMACSources().clear();
BOOST_FOREACH(ConstElementPtr source_elem, value->listValue()) {
std::string source_str = source_elem->stringValue();
try {
source = CfgMACSource::MACSourceFromText(source_str);
CfgMgr::instance().getStagingCfg()->getMACSources().add(source);
} catch (const std::exception& ex) {
isc_throw(DhcpConfigError, "Failed to convert '"
<< source_str << "' to any recognized MAC source:"
<< ex.what() << " (" << value->getPosition() << ")");
}
}
}
void
MACSourcesListConfigParser::commit() {
// Nothing to do.
}
// ******************** HooksLibrariesParser ************************* // ******************** HooksLibrariesParser *************************
HooksLibrariesParser::HooksLibrariesParser(const std::string& param_name) HooksLibrariesParser::HooksLibrariesParser(const std::string& param_name)

View File

@ -382,12 +382,10 @@ private:
/// @brief parser for interface list definition /// @brief parser for interface list definition
/// ///
/// This parser handles Dhcp4/interface entry. /// This parser handles Dhcp4/interfaces and Dhcp6/interfaces entries.
/// It contains a list of network interfaces that the server listens on. /// It contains a list of network interfaces that the server listens on.
/// In particular, it can contain an entry called "all" or "any" that /// In particular, it can contain an entry called "all" or "any" that
/// designates all interfaces. /// designates all interfaces.
///
/// It is useful for parsing Dhcp4/interface parameter.
class InterfaceListConfigParser : public DhcpConfigParser { class InterfaceListConfigParser : public DhcpConfigParser {
public: public:
@ -422,6 +420,48 @@ private:
ParserContextPtr global_context_; ParserContextPtr global_context_;
}; };
/// @brief parser for MAC/hardware aquisition sources
///
/// This parser handles Dhcp6/mac-sources entry.
/// It contains a list of MAC/hardware aquisition source, i.e. methods how
/// MAC address can possibly by obtained in DHCPv6. For a currently supported
/// methods, see @ref isc::dhcp::Pkt::getMAC.
class MACSourcesListConfigParser : public DhcpConfigParser {
public:
/// @brief constructor
///
/// As this is a dedicated parser, it must be used to parse
/// "mac-sources" parameter only. All other types will throw exception.
///
/// @param param_name name of the configuration parameter being parsed
/// @param global_context Global parser context.
/// @throw BadValue if supplied parameter name is not "mac-sources"
MACSourcesListConfigParser(const std::string& param_name,
ParserContextPtr global_context);
/// @brief parses parameters value
///
/// Parses configuration entry (list of sources) and adds each element
/// to the sources list.
///
/// @param value pointer to the content of parsed values
virtual void build(isc::data::ConstElementPtr value);
/// @brief Does nothing.
virtual void commit();
private:
// Parsed parameter name
std::string param_name_;
/// Global parser context.
ParserContextPtr global_context_;
};
/// @brief Parser for hooks library list /// @brief Parser for hooks library list
/// ///
/// This parser handles the list of hooks libraries. This is an optional list, /// This parser handles the list of hooks libraries. This is an optional list,
@ -1172,4 +1212,3 @@ typedef boost::shared_ptr<Uint32Parser> Uint32ParserPtr;
}; // end of isc namespace }; // end of isc namespace
#endif // DHCP_PARSERS_H #endif // DHCP_PARSERS_H

View File

@ -16,6 +16,7 @@
#include <dhcpsrv/srv_config.h> #include <dhcpsrv/srv_config.h>
#include <log/logger_manager.h> #include <log/logger_manager.h>
#include <log/logger_specification.h> #include <log/logger_specification.h>
#include <dhcp/pkt.h> // Needed for HWADDR_SOURCE_*
#include <list> #include <list>
#include <sstream> #include <sstream>

View File

@ -21,6 +21,7 @@
#include <dhcpsrv/cfg_option_def.h> #include <dhcpsrv/cfg_option_def.h>
#include <dhcpsrv/cfg_subnets4.h> #include <dhcpsrv/cfg_subnets4.h>
#include <dhcpsrv/cfg_subnets6.h> #include <dhcpsrv/cfg_subnets6.h>
#include <dhcpsrv/cfg_mac_source.h>
#include <dhcpsrv/logging_info.h> #include <dhcpsrv/logging_info.h>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <vector> #include <vector>
@ -237,6 +238,22 @@ public:
//@} //@}
/// @brief Returns non-const reference to an array that stores
/// MAC/hardware address sources.
///
/// @return non-const reference to MAC/hardware address sources
CfgMACSource& getMACSources() {
return (cfg_mac_source_);
}
/// @brief Returns const reference to an array that stores
/// MAC/hardware address sources.
///
/// @return const reference to MAC/hardware address sources
const CfgMACSource& getMACSources() const {
return (cfg_mac_source_);
}
/// @brief Copies the currnet configuration to a new configuration. /// @brief Copies the currnet configuration to a new configuration.
/// ///
/// This method copies the parameters stored in the configuration to /// This method copies the parameters stored in the configuration to
@ -310,6 +327,7 @@ public:
//@} //@}
private: private:
/// @brief Sequence number identifying the configuration. /// @brief Sequence number identifying the configuration.
@ -348,6 +366,8 @@ private:
/// reservations for different IPv4 and IPv6 subnets. /// reservations for different IPv4 and IPv6 subnets.
CfgHostsPtr cfg_hosts_; CfgHostsPtr cfg_hosts_;
/// @brief A list of configured MAC sources.
CfgMACSource cfg_mac_source_;
}; };
/// @name Pointers to the @c SrvConfig object. /// @name Pointers to the @c SrvConfig object.

View File

@ -57,6 +57,7 @@ libdhcpsrv_unittests_SOURCES += alloc_engine_unittest.cc
libdhcpsrv_unittests_SOURCES += callout_handle_store_unittest.cc libdhcpsrv_unittests_SOURCES += callout_handle_store_unittest.cc
libdhcpsrv_unittests_SOURCES += cfg_hosts_unittest.cc libdhcpsrv_unittests_SOURCES += cfg_hosts_unittest.cc
libdhcpsrv_unittests_SOURCES += cfg_iface_unittest.cc libdhcpsrv_unittests_SOURCES += cfg_iface_unittest.cc
libdhcpsrv_unittests_SOURCES += cfg_mac_source_unittest.cc
libdhcpsrv_unittests_SOURCES += cfg_option_unittest.cc libdhcpsrv_unittests_SOURCES += cfg_option_unittest.cc
libdhcpsrv_unittests_SOURCES += cfg_option_def_unittest.cc libdhcpsrv_unittests_SOURCES += cfg_option_def_unittest.cc
libdhcpsrv_unittests_SOURCES += cfg_subnets4_unittest.cc libdhcpsrv_unittests_SOURCES += cfg_subnets4_unittest.cc
@ -120,7 +121,8 @@ if USE_CLANGPP
libdhcpsrv_unittests_CXXFLAGS += -Wno-unused-variable -Wno-unused-parameter libdhcpsrv_unittests_CXXFLAGS += -Wno-unused-variable -Wno-unused-parameter
endif endif
libdhcpsrv_unittests_LDADD = $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la libdhcpsrv_unittests_LDADD = $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/testutils/libdhcpsrvtest.la libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/testutils/libdhcpsrvtest.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/tests/libdhcptest.la libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/tests/libdhcptest.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la

View File

@ -0,0 +1,54 @@
// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#include <dhcpsrv/cfg_mac_source.h>
#include <dhcp/hwaddr.h>
#include <exceptions/exceptions.h>
#include <gtest/gtest.h>
namespace {
using namespace isc;
using namespace isc::dhcp;
// Checks whether CfgMACSource::MACSourceFromText is working correctly.
// Technically, this is a Pkt, not Pkt6 test, but since there is no separate
// unit-tests for Pkt and it is abstract, so it would be tricky to test it
// directly. Hence test is being run in Pkt6.
TEST(CfgMACSourceTest, MACSourceFromText) {
EXPECT_THROW(CfgMACSource::MACSourceFromText("unknown"), BadValue);
EXPECT_EQ(HWAddr::HWADDR_SOURCE_ANY, CfgMACSource::MACSourceFromText("any"));
EXPECT_EQ(HWAddr::HWADDR_SOURCE_RAW, CfgMACSource::MACSourceFromText("raw"));
EXPECT_EQ(HWAddr::HWADDR_SOURCE_DUID, CfgMACSource::MACSourceFromText("duid"));
EXPECT_EQ(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL,
CfgMACSource::MACSourceFromText("ipv6-link-local"));
EXPECT_EQ(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION,
CfgMACSource::MACSourceFromText("client-link-addr-option"));
EXPECT_EQ(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION,
CfgMACSource::MACSourceFromText("rfc6939"));
EXPECT_EQ(HWAddr::HWADDR_SOURCE_REMOTE_ID,
CfgMACSource::MACSourceFromText("remote-id"));
EXPECT_EQ(HWAddr::HWADDR_SOURCE_REMOTE_ID,
CfgMACSource::MACSourceFromText("rfc4649"));
EXPECT_EQ(HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID,
CfgMACSource::MACSourceFromText("subscriber-id"));
EXPECT_EQ(HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID,
CfgMACSource::MACSourceFromText("rfc4580"));
EXPECT_EQ(HWAddr::HWADDR_SOURCE_DOCSIS,
CfgMACSource::MACSourceFromText("docsis"));
}
};

View File

@ -22,6 +22,7 @@
#include <dhcp/tests/iface_mgr_test_config.h> #include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcpsrv/cfgmgr.h> #include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/subnet.h> #include <dhcpsrv/subnet.h>
#include <dhcpsrv/cfg_mac_source.h>
#include <dhcpsrv/parsers/dhcp_parsers.h> #include <dhcpsrv/parsers/dhcp_parsers.h>
#include <dhcpsrv/tests/test_libraries.h> #include <dhcpsrv/tests/test_libraries.h>
#include <dhcpsrv/testutils/config_result_check.h> #include <dhcpsrv/testutils/config_result_check.h>
@ -212,7 +213,7 @@ TEST_F(DhcpParserTest, uint32ParserTest) {
EXPECT_EQ(test_value, actual_value); EXPECT_EQ(test_value, actual_value);
} }
/// @brief Check InterfaceListParser basic functionality /// @brief Check InterfaceListConfigParser basic functionality
/// ///
/// Verifies that the parser: /// Verifies that the parser:
/// 1. Does not allow empty for storage. /// 1. Does not allow empty for storage.
@ -270,6 +271,49 @@ TEST_F(DhcpParserTest, interfaceListParserTest) {
EXPECT_TRUE(test_config.socketOpen("eth1", AF_INET)); EXPECT_TRUE(test_config.socketOpen("eth1", AF_INET));
} }
/// @brief Check MACSourcesListConfigParser basic functionality
///
/// Verifies that the parser:
/// 1. Does not allow empty for storage.
/// 2. Does not allow name other than "mac-sources"
/// 3. Parses list of mac sources and adds them to CfgMgr
TEST_F(DhcpParserTest, MacSourcesListConfigParserTest) {
const std::string valid_name = "mac-sources";
const std::string bogus_name = "bogus-name";
ParserContextPtr parser_context(new ParserContext(Option::V6));
// Verify that parser constructor fails if parameter name isn't "mac-sources"
EXPECT_THROW(MACSourcesListConfigParser(bogus_name, parser_context),
isc::BadValue);
// That's an equivalent of the following snippet:
// "mac-sources: [ \"duid\", \"ipv6\" ]";
ElementPtr config = Element::createList();
config->add(Element::create("duid"));
config->add(Element::create("ipv6-link-local"));
boost::scoped_ptr<MACSourcesListConfigParser>
parser(new MACSourcesListConfigParser(valid_name, parser_context));
// This should parse the configuration and add eth0 and eth1 to the list
// of interfaces that server should listen on.
EXPECT_NO_THROW(parser->build(config));
EXPECT_NO_THROW(parser->commit());
// Use CfgMgr instance to check if eth0 and eth1 was added, and that
// eth2 was not added.
SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg();
ASSERT_TRUE(cfg);
CfgMACSources configured_sources = cfg->getMACSources().get();
ASSERT_EQ(2, configured_sources.size());
EXPECT_EQ(HWAddr::HWADDR_SOURCE_DUID, configured_sources[0]);
EXPECT_EQ(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL, configured_sources[1]);
}
/// @brief Test Fixture class which provides basic structure for testing /// @brief Test Fixture class which provides basic structure for testing
/// configuration parsing. This is essentially the same structure provided /// configuration parsing. This is essentially the same structure provided
/// by dhcp servers. /// by dhcp servers.