]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5502] Added FNV-1a 64 bit hash and replaced MD5 in CQL host dats source
authorFrancis Dupont <fdupont@isc.org>
Tue, 20 Feb 2018 15:32:45 +0000 (16:32 +0100)
committerFrancis Dupont <fdupont@isc.org>
Tue, 20 Feb 2018 15:32:45 +0000 (16:32 +0100)
src/lib/dhcpsrv/Makefile.am
src/lib/dhcpsrv/cql_host_data_source.cc
src/lib/util/Makefile.am
src/lib/util/hash.h [new file with mode: 0644]
src/lib/util/tests/Makefile.am
src/lib/util/tests/hash_unittest.cc [new file with mode: 0644]

index 037843bdb4990d3c7e7c0bc016a85acb8a15f3a0..2816ea02793a11628c5d9bae9ce20be7c66aad9f 100644 (file)
@@ -17,7 +17,7 @@ if HAVE_PGSQL
 AM_CPPFLAGS += $(PGSQL_CPPFLAGS)
 endif
 if HAVE_CQL
-AM_CPPFLAGS += $(CQL_CPPFLAGS) $(CRYPTO_INCLUDES)
+AM_CPPFLAGS += $(CQL_CPPFLAGS)
 endif
 
 AM_CXXFLAGS = $(KEA_CXXFLAGS)
index 3109794421476fa7c6146435708227e9ac1d65a0..e9073848b1b9f02847b0623ac5820911d07cc307 100644 (file)
 #include <dhcpsrv/db_exceptions.h>
 #include <dhcpsrv/dhcpsrv_log.h>
 #include <util/buffer.h>
+#include <util/hash.h>
 #include <util/optional_value.h>
 #include <asiolink/io_address.h>
 
-/// @todo: With this include, Cassandra backend requires compilation with openssl.
-/// Kea supports two crypto libs: openssl and botan. The abstraction layer provided
-/// is via cryptolink.
-#include <openssl/md5.h>  // for MD5_DIGEST_LENGTH
 #include <stdint.h>       // for uint64_t
 
 #include <boost/algorithm/string/classification.hpp>  // for boost::is_any_of
@@ -185,9 +182,8 @@ public:
     /// @brief Create unique hash for storage in table id.
     ///
     /// Hash function used for creating a pseudo-unique hash from member
-    /// values which uniquely determine an entry in the table. Uses OpenSSL's
-    /// MD5 implementation.
-    /// @todo: This must be generic and use cryptolink wrapper. See ticket #5502.
+    /// values which uniquely determine an entry in the table. Uses FNV-1a
+    /// on 64 bits.
     ///
     /// The primary key aggregates: host_ipv4_subnet_id, host_ipv6_subnet_id,
     /// host_ipv4_address, reserved_ipv6_prefix_address,
@@ -859,50 +855,25 @@ CqlHostExchange::createBindForMutation(const HostPtr& host,
     }
 }
 
-uint64_t
-md5Hash(const std::string& input) {
-
-    /// @todo: Convert this code to cryptolink calls and replace the
-    /// direct use fromn md5.
-
-    // Prepare structures for MD5().
-    const size_t word_size = MD5_DIGEST_LENGTH / sizeof(uint64_t);
-    uint64_t hash[word_size];
-    unsigned char* digest = reinterpret_cast<unsigned char*>(hash);
-    unsigned char* string = reinterpret_cast<unsigned char*>(const_cast<char*>(input.c_str()));
-    std::fill(hash, hash + word_size, 0);
-
-    // Get MD5 hash value.
-    MD5(string, input.size(), digest);
-
-    // Return the first part of the hash value which still retains all
-    // properties of the full hash value.
-    return (hash[0]);
-}
-
 cass_int64_t
 CqlHostExchange::hashIntoId() const {
-    // Allocates a fixed maximum length in the stringstream for each
-    // aggregated field to avoid collisions between distinct entries.
+    // Add a separator between aggregated field to avoid collisions
+    // between distinct entries.
 
     // Get key.
     std::stringstream key_stream;
-    key_stream << std::setw(10) << std::setfill('-') << host_ipv4_subnet_id_;
-    key_stream << std::setw(10) << std::setfill('-') << host_ipv6_subnet_id_;
-    key_stream << std::setw(V4ADDRESS_TEXT_MAX_LEN) << std::setfill('-')
-               << host_ipv4_address_;
-    key_stream << std::setw(V6ADDRESS_TEXT_MAX_LEN) << std::setfill('-')
-               << reserved_ipv6_prefix_address_;
-    key_stream << std::setw(4) << std::setfill('-')
-               << reserved_ipv6_prefix_length_;
-    key_stream << std::setw(4) << std::setfill('-') << option_code_;
-    key_stream << std::setw(OPTION_SPACE_MAX_LENGTH) << std::setfill('-')
-               << option_space_;
+    key_stream << host_ipv4_subnet_id_ << "-";
+    key_stream << host_ipv6_subnet_id_ << "-";
+    key_stream << host_ipv4_address_ << "-";
+    key_stream << reserved_ipv6_prefix_address_ << "/";
+    key_stream << reserved_ipv6_prefix_length_ << "-";
+    key_stream << option_code_ << "-";
+    key_stream << option_space_;
     const std::string key = key_stream.str();
 
-    const cass_int64_t md5 = static_cast<cass_int64_t>(md5Hash(key));
+    const cass_int64_t hash = static_cast<cass_int64_t>(Hash64::hash(key));
 
-    return (md5);
+    return (hash);
 }
 
 boost::any
@@ -1287,7 +1258,7 @@ private:
 
 /// @brief hash function for HostMap
 ///
-/// Returns a 64-bits key value. The key is generated with MD5 hash
+/// Returns a 64-bits key value. The key is generated with FNV-1a 64 bit
 /// algorithm.
 ///
 /// @param key being hashed
@@ -1298,21 +1269,16 @@ hash_value(const HostKey& key) {
     // Get key.
     std::stringstream key_stream;
     HostIdentifier host_identifier = std::get<HOST_IDENTIFIER>(key);
-    key_stream << std::setw(DUID::MAX_DUID_LEN) << std::setfill('0')
-               << DUID(host_identifier).toText();
-    key_stream << std::setw(2) << std::setfill('0')
-               << std::get<HOST_IDENTIFIER_TYPE>(key);
-    key_stream << std::setw(10) << std::setfill('0')
-               << std::get<IPv4_SUBNET_ID>(key);
-    key_stream << std::setw(10) << std::setfill('0')
-               << std::get<IPv6_SUBNET_ID>(key);
-    key_stream << std::setw(V4ADDRESS_TEXT_MAX_LEN) << std::setfill('0')
-               << std::get<IPv4_RESERVATION>(key);
+    key_stream << DUID(host_identifier).toText() << "-";
+    key_stream << std::get<HOST_IDENTIFIER_TYPE>(key) << "-";
+    key_stream << std::get<IPv4_SUBNET_ID>(key) << "-";
+    key_stream << std::get<IPv6_SUBNET_ID>(key) << "-";
+    key_stream << std::get<IPv4_RESERVATION>(key);
     const std::string key_string = key_stream.str();
 
-    const uint64_t md5 = md5Hash(key_string);
+    const uint64_t hash = Hash64::hash(key_string);
 
-    return (static_cast<std::size_t>(md5));
+    return (static_cast<std::size_t>(hash));
 }
 
 /// @brief equals operator for HostKey
index 7c0bac0096bd097288f5c2ac0946e50b6708d919..f2c684f3ca16cf174a9b13f84ab8a9e137316e2e 100644 (file)
@@ -11,6 +11,7 @@ libkea_util_la_SOURCES  = boost_time_utils.h boost_time_utils.cc
 libkea_util_la_SOURCES += buffer.h io_utilities.h
 libkea_util_la_SOURCES += csv_file.h csv_file.cc
 libkea_util_la_SOURCES += filename.h filename.cc
+libkea_util_la_SOURCES += hash.h
 libkea_util_la_SOURCES += labeled_value.h labeled_value.cc
 libkea_util_la_SOURCES += memory_segment.h
 libkea_util_la_SOURCES += memory_segment_local.h memory_segment_local.cc
@@ -50,6 +51,7 @@ libkea_util_include_HEADERS = \
        buffer.h \
        csv_file.h \
        filename.h \
+       hash.h \
        io_utilities.h \
        labeled_value.h \
        memory_segment.h \
diff --git a/src/lib/util/hash.h b/src/lib/util/hash.h
new file mode 100644 (file)
index 0000000..45d6ba5
--- /dev/null
@@ -0,0 +1,57 @@
+// Copyright (C) 2018 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef HASH_H
+#define HASH_H
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+
+namespace isc {
+namespace util {
+
+/// @brief Hash implementation based on Fowler-Noll-Vo hash function
+///
+struct Hash64 {
+    /// @brief Compute the hash
+    ///
+    /// FNV-1a hash function
+    ///
+    /// @param data data to hash
+    /// @param length length of data
+    /// @return the hash value
+    static uint64_t hash(const uint8_t* data, size_t length) {
+        uint64_t hash = FNV_offset_basis;
+        for (size_t i = 0; i < length; ++i) {
+            hash = hash ^ data[i];
+            hash = hash * FNV_prime;
+        }
+        return (hash);
+    }
+
+    /// @brief Compute the hash
+    ///
+    /// FNV-1a hash function
+    ///
+    /// @param str not empty string to hash
+    /// @return the hash value
+    static uint64_t hash(const std::string& str) {
+        return (hash(reinterpret_cast<const uint8_t*>(str.c_str()),
+                     str.size()));
+    }
+
+    /// @brief Offset basis
+    static const uint64_t FNV_offset_basis = 14695981039346656037ull;
+
+    /// @brief Prime
+    static const uint64_t FNV_prime = 1099511628211ull;
+};
+
+} // end of namespace isc::util
+} // end of namespace isc
+
+#endif
index 301d94f2bbab9eea5407b6abd8b71fb17dded5f5..c6b7173bfa38bf57e4603203bd72b90eb84dba66 100644 (file)
@@ -36,6 +36,7 @@ run_unittests_SOURCES += csv_file_unittest.cc
 run_unittests_SOURCES += fd_share_tests.cc
 run_unittests_SOURCES += fd_tests.cc
 run_unittests_SOURCES += filename_unittest.cc
+run_unittests_SOURCES += hash_unittest.cc
 run_unittests_SOURCES += hex_unittest.cc
 run_unittests_SOURCES += io_utilities_unittest.cc
 run_unittests_SOURCES += labeled_value_unittest.cc
diff --git a/src/lib/util/tests/hash_unittest.cc b/src/lib/util/tests/hash_unittest.cc
new file mode 100644 (file)
index 0000000..f789e51
--- /dev/null
@@ -0,0 +1,34 @@
+// Copyright (C) 2018 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <util/hash.h>
+
+#include <gtest/gtest.h>
+
+#include <cstring>
+#include <vector>
+
+using namespace isc::util;
+using namespace std;
+
+namespace {
+
+TEST(HashTest, empty) {
+    EXPECT_EQ(14695981039346656037ull, Hash64::hash(0, 0));
+}
+
+TEST(HashTest, foobar) {
+    EXPECT_EQ(9625390261332436968ull, Hash64::hash(string("foobar")));
+}
+
+TEST(HashTest, chongo) {
+    EXPECT_EQ(5080352029159061781ull,
+              Hash64::hash(string("chongo was here!\n")));
+}
+
+}