diff --git a/doc/Makefile.am b/doc/Makefile.am
index 8ad633cb4c..173f563648 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -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
diff --git a/doc/examples/kea4/multiple-options.json b/doc/examples/kea4/multiple-options.json
index 9b125dc1b9..4130e4c5e6 100644
--- a/doc/examples/kea4/multiple-options.json
+++ b/doc/examples/kea4/multiple-options.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",
diff --git a/doc/examples/kea4/several-subnets.json b/doc/examples/kea4/several-subnets.json
index aff824b640..b8576a9f23 100644
--- a/doc/examples/kea4/several-subnets.json
+++ b/doc/examples/kea4/several-subnets.json
@@ -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
diff --git a/doc/examples/kea4/single-subnet.json b/doc/examples/kea4/single-subnet.json
index 0c2e10ca07..28368939d1 100644
--- a/doc/examples/kea4/single-subnet.json
+++ b/doc/examples/kea4/single-subnet.json
@@ -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"
+ }
]
},
diff --git a/doc/examples/kea6/advanced.json b/doc/examples/kea6/advanced.json
new file mode 100644
index 0000000000..1923064657
--- /dev/null
+++ b/doc/examples/kea6/advanced.json
@@ -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"
+ }
+ ]
+}
+
+}
diff --git a/doc/examples/kea6/multiple-options.json b/doc/examples/kea6/multiple-options.json
index 682b3b5102..e30cf9d303 100644
--- a/doc/examples/kea6/multiple-options.json
+++ b/doc/examples/kea6/multiple-options.json
@@ -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",
diff --git a/doc/examples/kea6/several-subnets.json b/doc/examples/kea6/several-subnets.json
index f138c059ea..d9744053fe 100644
--- a/doc/examples/kea6/several-subnets.json
+++ b/doc/examples/kea6/several-subnets.json
@@ -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
diff --git a/doc/examples/kea6/simple.json b/doc/examples/kea6/simple.json
index e266d0ba10..cffec39f02 100644
--- a/doc/examples/kea6/simple.json
+++ b/doc/examples/kea6/simple.json
@@ -1,13 +1,13 @@
# 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"
}
]
},
diff --git a/doc/guide/dhcp6-srv.xml b/doc/guide/dhcp6-srv.xml
index 8abb16c373..e1974a06c2 100644
--- a/doc/guide/dhcp6-srv.xml
+++ b/doc/guide/dhcp6-srv.xml
@@ -1838,6 +1838,117 @@ should include options from the isc option space:
+
+ MAC/Hardware addresses in DHCPv6
+ 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.
+
+ 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 "any" 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:
+
+"Dhcp6": {
+ "mac-sources": [ "method1", "method2", "method3", ... ],
+
+ "subnet6": [ ... ],
+
+ ...
+}
+
+
+ When not specified, a special value of any 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.
+
+ Supported methods are:
+
+
+ any - 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 mac-sources are defined.
+
+
+
+ raw 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.
+
+
+
+ duid - 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.
+
+
+
+ ipv6-link-local - 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.
+
+
+
+ client-link-addr-option - One extension defined
+ to alleviate missing MAC issues is client link-layer address option, defined
+ in RFC 6939. 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 rfc6939,
+ which is an alias for client-link-addr-option.
+
+
+
+ remote-id RFC 4649
+ 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
+ rfc4649, which is an alias for remote-id.
+
+
+
+ subscriber-id - Another option
+ that is somewhat similar to the previous one is subscriber-id,
+ defined in RFC
+ 4580. It is, too, inserted by a relay agent that is
+ configured to insert it. This parameter can also be specified
+ as rfc4580, which is an alias for
+ subscriber-id. This method is currently not
+ implemented.
+
+
+
+ docsis - 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.
+
+
+
+
+
Supported DHCPv6 Standards
diff --git a/src/bin/dhcp6/dhcp6.spec b/src/bin/dhcp6/dhcp6.spec
index f0fe861b90..ff2b308b42 100644
--- a/src/bin/dhcp6/dhcp6.spec
+++ b/src/bin/dhcp6/dhcp6.spec
@@ -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,
diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc
index 8b5336cde5..fc44347866 100644
--- a/src/bin/dhcp6/dhcp6_srv.cc
+++ b/src/bin/dhcp6/dhcp6_srv.cc
@@ -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());
diff --git a/src/bin/dhcp6/dhcp6_srv.h b/src/bin/dhcp6/dhcp6_srv.h
index af355c0d7a..ff3ff69581 100644
--- a/src/bin/dhcp6/dhcp6_srv.h
+++ b/src/bin/dhcp6/dhcp6_srv.h
@@ -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
///
diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc
index eaf278a199..1ea45bacd9 100644
--- a/src/bin/dhcp6/json_config_parser.cc
+++ b/src/bin/dhcp6/json_config_parser.cc
@@ -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: "
diff --git a/src/bin/dhcp6/json_config_parser.h b/src/bin/dhcp6/json_config_parser.h
index d3c1b27f3f..3970d68e94 100644
--- a/src/bin/dhcp6/json_config_parser.h
+++ b/src/bin/dhcp6/json_config_parser.h
@@ -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
#include
#include
diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc
index c3bf74ece6..8682d0b57c 100644
--- a/src/bin/dhcp6/tests/config_parser_unittest.cc
+++ b/src/bin/dhcp6/tests/config_parser_unittest.cc
@@ -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);
+}
+
};
diff --git a/src/bin/dhcp6/tests/dhcp6_client.cc b/src/bin/dhcp6/tests/dhcp6_client.cc
index b4feb3c199..4e5a9108a8 100644
--- a/src/bin/dhcp6/tests/dhcp6_client.cc
+++ b/src/bin/dhcp6/tests/dhcp6_client.cc
@@ -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();
diff --git a/src/lib/dhcp/hwaddr.cc b/src/lib/dhcp/hwaddr.cc
index 6be98a6770..5d76ec723d 100644
--- a/src/lib/dhcp/hwaddr.cc
+++ b/src/lib/dhcp/hwaddr.cc
@@ -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) {
}
diff --git a/src/lib/dhcp/hwaddr.h b/src/lib/dhcp/hwaddr.h
index 0c7f21675a..3aa2046a1c 100644
--- a/src/lib/dhcp/hwaddr.h
+++ b/src/lib/dhcp/hwaddr.h
@@ -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;
+
+ /// @}
+
/// @brief default constructor
HWAddr();
diff --git a/src/lib/dhcp/pkt.cc b/src/lib/dhcp/pkt.cc
index 6bb0c8e386..72e0f86742 100644
--- a/src/lib/dhcp/pkt.cc
+++ b/src/lib/dhcp/pkt.cc
@@ -15,6 +15,7 @@
#include
#include
#include
+#include
#include
namespace isc {
@@ -129,11 +130,11 @@ Pkt::getMAC(uint32_t hw_addr_src) {
HWAddrPtr mac;
// Method 1: from raw sockets.
- if (hw_addr_src & HWADDR_SOURCE_RAW) {
+ if (hw_addr_src & HWAddr::HWADDR_SOURCE_RAW) {
mac = getRemoteHWAddr();
if (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,
// there's no point in trying other options.
return (HWAddrPtr());
@@ -143,11 +144,11 @@ Pkt::getMAC(uint32_t hw_addr_src) {
// Method 2: Extracted from DUID-LLT or DUID-LL
// 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();
if (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
// info, there's no point in trying other options.
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
- if (hw_addr_src & HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION) {
+ if (hw_addr_src & HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION) {
mac = getMACFromIPv6RelayOpt();
if (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
// of that info, there's no point in trying other options.
return (HWAddrPtr());
@@ -223,6 +224,5 @@ Pkt::getMACFromIPv6(const isc::asiolink::IOAddress& addr) {
return (HWAddrPtr(new HWAddr(bin, hwtype)));
}
-
};
};
diff --git a/src/lib/dhcp/pkt.h b/src/lib/dhcp/pkt.h
index 21626b0d35..2b5e73f4bf 100644
--- a/src/lib/dhcp/pkt.h
+++ b/src/lib/dhcp/pkt.h
@@ -37,58 +37,6 @@ namespace dhcp {
/// @note This is abstract class. Please instantiate derived classes
/// such as @c Pkt4 or @c Pkt6.
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:
/// @brief Constructor.
diff --git a/src/lib/dhcp/tests/pkt4_unittest.cc b/src/lib/dhcp/tests/pkt4_unittest.cc
index ae39d8906b..1ebf77125a 100644
--- a/src/lib/dhcp/tests/pkt4_unittest.cc
+++ b/src/lib/dhcp/tests/pkt4_unittest.cc
@@ -857,8 +857,8 @@ TEST_F(Pkt4Test, getMAC) {
Pkt4 pkt(DHCPOFFER, 1234);
// DHCPv4 packet by default doens't have MAC address specified.
- EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_ANY));
- EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_RAW));
+ EXPECT_FALSE(pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY));
+ EXPECT_FALSE(pkt.getMAC(HWAddr::HWADDR_SOURCE_RAW));
// Let's invent a MAC
const uint8_t hw[] = { 2, 4, 6, 8, 10, 12 }; // MAC
@@ -869,12 +869,12 @@ TEST_F(Pkt4Test, getMAC) {
pkt.setRemoteHWAddr(dummy_hwaddr);
// Now we should be able to get something
- ASSERT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_ANY));
- ASSERT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_RAW));
+ ASSERT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY));
+ ASSERT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_RAW));
// 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(Pkt::HWADDR_SOURCE_RAW));
+ ASSERT_TRUE(*dummy_hwaddr == *pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY));
+ ASSERT_TRUE(*dummy_hwaddr == *pkt.getMAC(HWAddr::HWADDR_SOURCE_RAW));
}
} // end of anonymous namespace
diff --git a/src/lib/dhcp/tests/pkt6_unittest.cc b/src/lib/dhcp/tests/pkt6_unittest.cc
index a2c3615184..a015c04f3b 100644
--- a/src/lib/dhcp/tests/pkt6_unittest.cc
+++ b/src/lib/dhcp/tests/pkt6_unittest.cc
@@ -883,20 +883,20 @@ TEST_F(Pkt6Test, getMAC) {
Pkt6 pkt(DHCPV6_ADVERTISE, 1234);
// DHCPv6 packet by default doens't have MAC address specified.
- EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_ANY));
- EXPECT_FALSE(pkt.getMAC(Pkt::HWADDR_SOURCE_RAW));
+ EXPECT_FALSE(pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY));
+ EXPECT_FALSE(pkt.getMAC(HWAddr::HWADDR_SOURCE_RAW));
// We haven't specified source IPv6 address, so this method should
// 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.
IOAddress linklocal_eui64("fe80::204:06ff:fe08:0a0c");
pkt.setRemoteAddr(linklocal_eui64);
- EXPECT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_ANY));
- EXPECT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL));
- EXPECT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL |
- Pkt::HWADDR_SOURCE_RAW));
+ EXPECT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY));
+ EXPECT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL));
+ EXPECT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL |
+ HWAddr::HWADDR_SOURCE_RAW));
pkt.setRemoteAddr(IOAddress("::"));
// Let's invent a MAC
@@ -908,14 +908,14 @@ TEST_F(Pkt6Test, getMAC) {
pkt.setRemoteHWAddr(dummy_hwaddr);
// Now we should be able to get something
- ASSERT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_ANY));
- ASSERT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_RAW));
- EXPECT_TRUE(pkt.getMAC(Pkt::HWADDR_SOURCE_IPV6_LINK_LOCAL |
- Pkt::HWADDR_SOURCE_RAW));
+ ASSERT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY));
+ ASSERT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_RAW));
+ EXPECT_TRUE(pkt.getMAC(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL |
+ HWAddr::HWADDR_SOURCE_RAW));
// 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(Pkt::HWADDR_SOURCE_RAW));
+ ASSERT_TRUE(*dummy_hwaddr == *pkt.getMAC(HWAddr::HWADDR_SOURCE_ANY));
+ ASSERT_TRUE(*dummy_hwaddr == *pkt.getMAC(HWAddr::HWADDR_SOURCE_RAW));
}
// 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
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
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);
stringstream tmp;
@@ -980,15 +980,15 @@ TEST_F(Pkt6Test, getMACFromIPv6LinkLocal_singleRelay) {
// If received from a global address, this method should fail
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
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
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);
stringstream tmp;
@@ -1041,7 +1041,7 @@ TEST_F(Pkt6Test, getMACFromIPv6LinkLocal_multiRelay) {
pkt.setIndex(iface->getIndex());
// 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);
// Let's check the info now.
@@ -1058,7 +1058,7 @@ TEST_F(Pkt6Test, getMACFromIPv6RelayOpt_singleRelay) {
Pkt6 pkt(DHCPV6_SOLICIT, 1234);
// 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.
Pkt6::RelayInfo info;
@@ -1075,7 +1075,7 @@ TEST_F(Pkt6Test, getMACFromIPv6RelayOpt_singleRelay) {
pkt.addRelayInfo(info);
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);
stringstream tmp;
@@ -1108,7 +1108,7 @@ TEST_F(Pkt6Test, getMACFromIPv6RelayOpt_multipleRelay) {
pkt.addRelayInfo(info2);
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)
// that inserts the correct client_linklayer_addr option.
@@ -1123,11 +1123,12 @@ TEST_F(Pkt6Test, getMACFromIPv6RelayOpt_multipleRelay) {
ASSERT_EQ(3, pkt.relay_info_.size());
// 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);
stringstream tmp;
tmp << "hwtype=1 fa:30:0b:fa:c0:fe";
EXPECT_EQ(tmp.str(), found->toText(true));
}
+
}
diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am
index 062ef8ef57..7e1cb28120 100644
--- a/src/lib/dhcpsrv/Makefile.am
+++ b/src/lib/dhcpsrv/Makefile.am
@@ -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_subnets4.cc cfg_subnets4.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 += csv_lease_file4.cc csv_lease_file4.h
libkea_dhcpsrv_la_SOURCES += csv_lease_file6.cc csv_lease_file6.h
diff --git a/src/lib/dhcpsrv/cfg_mac_source.cc b/src/lib/dhcpsrv/cfg_mac_source.cc
new file mode 100644
index 0000000000..715820bd07
--- /dev/null
+++ b/src/lib/dhcpsrv/cfg_mac_source.cc
@@ -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
+#include
+#include
+#include
+
+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.");
+}
+
+
+};
+};
diff --git a/src/lib/dhcpsrv/cfg_mac_source.h b/src/lib/dhcpsrv/cfg_mac_source.h
new file mode 100644
index 0000000000..3b3a098a86
--- /dev/null
+++ b/src/lib/dhcpsrv/cfg_mac_source.h
@@ -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
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Container for defined MAC/hardware address sources
+typedef std::vector 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
diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc
index 18043635c1..bf9a47731f 100644
--- a/src/lib/dhcpsrv/parsers/dhcp_parsers.cc
+++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.cc
@@ -17,6 +17,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -187,6 +188,7 @@ InterfaceListConfigParser(const std::string& param_name,
void
InterfaceListConfigParser::build(ConstElementPtr value) {
CfgIface cfg_iface;
+
BOOST_FOREACH(ConstElementPtr iface, value->listValue()) {
std::string iface_name = iface->stringValue();
try {
@@ -206,6 +208,45 @@ InterfaceListConfigParser::commit() {
// 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(const std::string& param_name)
diff --git a/src/lib/dhcpsrv/parsers/dhcp_parsers.h b/src/lib/dhcpsrv/parsers/dhcp_parsers.h
index 984633f20b..46e1f7f865 100644
--- a/src/lib/dhcpsrv/parsers/dhcp_parsers.h
+++ b/src/lib/dhcpsrv/parsers/dhcp_parsers.h
@@ -382,12 +382,10 @@ private:
/// @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.
/// In particular, it can contain an entry called "all" or "any" that
/// designates all interfaces.
-///
-/// It is useful for parsing Dhcp4/interface parameter.
class InterfaceListConfigParser : public DhcpConfigParser {
public:
@@ -422,6 +420,48 @@ private:
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
///
/// This parser handles the list of hooks libraries. This is an optional list,
@@ -1172,4 +1212,3 @@ typedef boost::shared_ptr Uint32ParserPtr;
}; // end of isc namespace
#endif // DHCP_PARSERS_H
-
diff --git a/src/lib/dhcpsrv/srv_config.cc b/src/lib/dhcpsrv/srv_config.cc
index a48268e927..ba2384db0f 100644
--- a/src/lib/dhcpsrv/srv_config.cc
+++ b/src/lib/dhcpsrv/srv_config.cc
@@ -16,6 +16,7 @@
#include
#include
#include
+#include // Needed for HWADDR_SOURCE_*
#include
#include
diff --git a/src/lib/dhcpsrv/srv_config.h b/src/lib/dhcpsrv/srv_config.h
index 8ffe29de87..b7403f4590 100644
--- a/src/lib/dhcpsrv/srv_config.h
+++ b/src/lib/dhcpsrv/srv_config.h
@@ -21,6 +21,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -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.
///
/// This method copies the parameters stored in the configuration to
@@ -310,6 +327,7 @@ public:
//@}
+
private:
/// @brief Sequence number identifying the configuration.
@@ -348,6 +366,8 @@ private:
/// reservations for different IPv4 and IPv6 subnets.
CfgHostsPtr cfg_hosts_;
+ /// @brief A list of configured MAC sources.
+ CfgMACSource cfg_mac_source_;
};
/// @name Pointers to the @c SrvConfig object.
diff --git a/src/lib/dhcpsrv/tests/Makefile.am b/src/lib/dhcpsrv/tests/Makefile.am
index a57545791c..8b7d5e212c 100644
--- a/src/lib/dhcpsrv/tests/Makefile.am
+++ b/src/lib/dhcpsrv/tests/Makefile.am
@@ -57,6 +57,7 @@ libdhcpsrv_unittests_SOURCES += alloc_engine_unittest.cc
libdhcpsrv_unittests_SOURCES += callout_handle_store_unittest.cc
libdhcpsrv_unittests_SOURCES += cfg_hosts_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_def_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
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/dhcp/tests/libdhcptest.la
libdhcpsrv_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
diff --git a/src/lib/dhcpsrv/tests/cfg_mac_source_unittest.cc b/src/lib/dhcpsrv/tests/cfg_mac_source_unittest.cc
new file mode 100644
index 0000000000..ad684aff13
--- /dev/null
+++ b/src/lib/dhcpsrv/tests/cfg_mac_source_unittest.cc
@@ -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
+#include
+#include
+#include
+
+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"));
+
+}
+
+};
diff --git a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
index a16e8f8c22..61b8e9d080 100644
--- a/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
+++ b/src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
@@ -22,6 +22,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -212,7 +213,7 @@ TEST_F(DhcpParserTest, uint32ParserTest) {
EXPECT_EQ(test_value, actual_value);
}
-/// @brief Check InterfaceListParser basic functionality
+/// @brief Check InterfaceListConfigParser basic functionality
///
/// Verifies that the parser:
/// 1. Does not allow empty for storage.
@@ -270,6 +271,49 @@ TEST_F(DhcpParserTest, interfaceListParserTest) {
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
+ 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
/// configuration parsing. This is essentially the same structure provided
/// by dhcp servers.