diff --git a/AUTHORS b/AUTHORS index 5054a13878..1abee51fc3 100644 --- a/AUTHORS +++ b/AUTHORS @@ -235,3 +235,8 @@ We have received the following contributions: - Brad Smith 2021-08: compilation fix for upcoming boost 1.77 + + - John Dickinson + 2021-11: Patch that adds support for v6 DUIDs to be embedded in v4 client + identifiers per RFC 4361. This allows Kea to support DDNS for + dual-stack clients per RFC 4703. diff --git a/ChangeLog b/ChangeLog index eb2894c51b..c5e966066f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +1971. [func] tmark,jad + Added support for embedded DHCPv6 DUIDs within DHCPv4 + Client Identifier options per RFC 4361. This allows + Kea to support DDNS in dual stack environments per + RFC 4703(Sec 5.2). Thanks to John Dickinson for + contributing the patch! + (Gitlab #1934) + Kea 2.1.1 (development) released on Nov 24, 2021 1970. [build] razvan diff --git a/doc/sphinx/arm/ddns.rst b/doc/sphinx/arm/ddns.rst index 3bd12bc058..5f5df5b481 100644 --- a/doc/sphinx/arm/ddns.rst +++ b/doc/sphinx/arm/ddns.rst @@ -79,7 +79,7 @@ the configuration parameter ``ddns-use-conflict-resolution``, supported by both ``kea-dhcp4`` and ``kea-dhcp6``. These servers use this parameter to set a flag within each NameChangeRequest they send that tells D2 whether conflict resolution should be employed for that request. -By default, conflict resolution is enabled. For more details, please refer +By default, conflict resolution is enabled. For more details, please refer to discussions of ``ddns-use-conflict-resolution`` in :ref:`dhcp4-ddns-config` and :ref:`dhcp6-ddns-config`. When conflict resolution is disabled, D2 still adds DHCID RRs but does @@ -97,9 +97,9 @@ issues that may arise with dual-stack clients. These are clients that wish to have both IPv4 and IPv6 mappings for the same FQDN. To work properly, clients must embed their IPv6 DUID within their IPv4 client identifier option, as described in `RFC -4703 `__. In this way, DNS updates -for both IPv4 and IPv6 can be managed under the same DHCID RR. Kea does not -currently support this feature. +4361 `__. In this way, DNS updates +for both IPv4 and IPv6 can be managed under the same DHCID RR. This feature +is supported by Kea beginning with release 2.1.2. .. _dhcp-ddns-server-start-stop: diff --git a/doc/sphinx/arm/dhcp4-srv.rst b/doc/sphinx/arm/dhcp4-srv.rst index c20d18a593..ccd2c03406 100644 --- a/doc/sphinx/arm/dhcp4-srv.rst +++ b/doc/sphinx/arm/dhcp4-srv.rst @@ -3666,7 +3666,10 @@ ones are: information stored in DHCPv4 and DHCPv6 servers for a particular host. Using common identification information by the DHCPv4 and DHCPv6 clients allows the network administrator to achieve this - correlation and better administer the network. + correlation and better administer the network. Beginning with + release 2.1.2, Kea supports DHCPv6 DUIDs embedded within DHCPv4 + Client Identifier options as described in + `RFC 4361 `__. DHCPv4 uses two distinct identifiers which are placed by the client in the queries sent to the server and copied by the server to its responses diff --git a/src/lib/dhcp_ddns/ncr_msg.cc b/src/lib/dhcp_ddns/ncr_msg.cc index 545a85f32f..cc595b4dc8 100644 --- a/src/lib/dhcp_ddns/ncr_msg.cc +++ b/src/lib/dhcp_ddns/ncr_msg.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -99,7 +99,32 @@ D2Dhcid::toStr() const { void D2Dhcid::fromClientId(const std::vector& clientid_data, const std::vector& wire_fqdn) { - createDigest(DHCID_ID_CLIENTID, clientid_data, wire_fqdn); + // IPv4 Client ID containing a DUID looks like this in RFC4361 + // Type IAID DUID + // +-----+----+----+----+----+----+----+--- + // | 255 | i1 | i2 | i3 | i4 | d1 | d2 |... + // +-----+----+----+----+----+----+----+--- + if (!clientid_data.empty() && clientid_data[0] == 255) { + if (clientid_data.size() <= 5) { + isc_throw(isc::dhcp_ddns::DhcidRdataComputeError, + "unable to compute DHCID from client identifier, embedded DUID " + "length of: " << clientid_data.size() << ", is too short"); + } + // RFC3315 states that the DUID is a type code of 2 octets followed + // by no more then 128 octets. So add the 5 from above and make sure + // the length is not too long. + if (clientid_data.size() > 135) { + isc_throw(isc::dhcp_ddns::DhcidRdataComputeError, + "unable to compute DHCID from client identifier, embedded DUID " + "length of: " << clientid_data.size() << ", is too long"); + } + std::vector::const_iterator start = clientid_data.begin() + 5; + std::vector::const_iterator end = clientid_data.end(); + std::vector duid(start, end); + createDigest(DHCID_ID_DUID, duid, wire_fqdn); + } else { + createDigest(DHCID_ID_CLIENTID, clientid_data, wire_fqdn); + } } void @@ -346,7 +371,7 @@ NameChangeRequest::fromJSON(const std::string& json) { // For backward compatibility use-conflict-resolution is optional // and defaults to true. - auto found = element_map.find("use-conflict-resolution"); + auto found = element_map.find("use-conflict-resolution"); if (found != element_map.end()) { ncr->setConflictResolution(found->second); } else { diff --git a/src/lib/dhcp_ddns/tests/ncr_unittests.cc b/src/lib/dhcp_ddns/tests/ncr_unittests.cc index ddbf0f631d..6ac2d09384 100644 --- a/src/lib/dhcp_ddns/tests/ncr_unittests.cc +++ b/src/lib/dhcp_ddns/tests/ncr_unittests.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2020 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -455,6 +455,48 @@ TEST_F(DhcidTest, fromClientId) { } +// This test verifies that DHCID is properly computed from a buffer holding +// client identifier data that contains a DUID. +TEST_F(DhcidTest, fromClientIdDUID) { + D2Dhcid dhcid; + + // Create a buffer holding client id.. + uint8_t clientid_data[] = { 0xff, 0x5d, 0xe2, 0x6c, 0x15, 0x00, 0x02, + 0x00, 0x00, 0xab, 0x11, 0x9a, 0x57, 0x20, 0x95, 0x71, 0x61, 0xbd, 0xd0 }; + std::vector clientid(clientid_data, + clientid_data + sizeof(clientid_data)); + + // Create DHCID. + ASSERT_NO_THROW(dhcid.fromClientId(clientid, wire_fqdn_)); + + // The reference DHCID (represented as string of hexadecimal digits) + std::string dhcid_ref = "000201A250D060B9352AE68E5014B78D25" + "1C30EFB0D5F64E48303B2BC56E6938F129E7"; + + // Make sure that the DHCID is valid. + EXPECT_EQ(dhcid_ref, dhcid.toStr()); + + // Make sure that a too long client identifier is not accepted. + clientid.resize(136); + EXPECT_THROW_MSG(dhcid.fromClientId(clientid, wire_fqdn_), + isc::dhcp_ddns::DhcidRdataComputeError, + "unable to compute DHCID from client identifier," + " embedded DUID length of: 136, is too long"); + + // Make sure that a too short client identifier is not accepted. + clientid.resize(5); + EXPECT_THROW_MSG(dhcid.fromClientId(clientid, wire_fqdn_), + isc::dhcp_ddns::DhcidRdataComputeError, + "unable to compute DHCID from client identifier," + " embedded DUID length of: 5, is too short"); + + // Make sure an empty client identifier is not accepted. + clientid.clear(); + EXPECT_THROW_MSG(dhcid.fromClientId(clientid, wire_fqdn_), + isc::dhcp_ddns::DhcidRdataComputeError, + "empty DUID used to create DHCID"); +} + // This test verifies that DHCID is properly computed from a HW address. TEST_F(DhcidTest, fromHWAddr) { D2Dhcid dhcid;