@@ -15,6 +15,7 @@
#include <rte_hash.h>
#include <rte_jhash.h>
#include <rte_hash_crc.h>
+#include <rte_siphash.h>
#include "test.h"
@@ -52,6 +53,42 @@ static uint32_t hash_values_crc[2][12] = {{
}
};
+static uint32_t hash_values_hsiphash[2][12] = {
+ {
+ 0x8e01e473, 0xc41e3669,
+ 0x813e4dbd, 0x7ebe2eea,
+ 0x33a5c5b7, 0xc910629b,
+ 0xba50237f, 0xbbc870c6,
+ 0x95124362, 0x850f8e0d,
+ 0x192ff266, 0xb41d8206,
+ }, {
+ 0x66200aa0, 0x769e7201,
+ 0x0e934d03, 0x96d7c892,
+ 0xb4643534, 0xcb758913,
+ 0x498b66e9, 0x116b4082,
+ 0x603030dc, 0x644b608b,
+ 0x74b29c27, 0x513f3a9c,
+ }
+};
+
+static uint64_t hash_values_siphash[2][12] = {
+ {
+ 0x8b5a0baa49fbc58d, 0x62c3506f27376c25,
+ 0xef7bdf3ee24abec8, 0xc72b1c24fc2f7938,
+ 0xc902632ed88f897f, 0xab631e00063006f5,
+ 0x7b821577565ea3a4, 0x8b19265d1c12cdc7,
+ 0x610e7ab6ada60b22, 0x3c6f1970dd62f235,
+ 0x4b8db19fd6940031, 0xa43827a530b08989,
+ }, {
+ 0x86e1bbae2893aaf1, 0x9c05614a696cda03,
+ 0x9ee31847083019c3, 0x600e860c97264e31,
+ 0xda8721038e4972bc, 0xfeedeb1b8bfe5d1e,
+ 0xb2d6388922af426f, 0x7d05b8e82e38cf30,
+ 0x439caa6ecd0b628d, 0x6d4c4ba6f3f2aed5,
+ 0x5622bb0da20658ff, 0x409d76de4adcd475,
+ }
+};
+
/*******************************************************************************
* Hash function performance test configuration section. Each performance test
* will be performed HASHTEST_ITERATIONS times.
@@ -61,9 +98,11 @@ static uint32_t hash_values_crc[2][12] = {{
*/
#define HASHTEST_ITERATIONS 1000000
#define MAX_KEYSIZE 64
-static rte_hash_function hashtest_funcs[] = {rte_jhash, rte_hash_crc};
-static uint32_t hashtest_initvals[] = {0, 0xdeadbeef};
-static uint32_t hashtest_key_lens[] = {
+static const rte_hash_function hashtest_funcs[] = {rte_jhash, rte_hash_crc, rte_hsiphash};
+static const uint32_t hashtest_initvals[] = {0, 0xdeadbeef};
+static const uint64_t hashtest_initkeys[] = {0, UINT64_C(0xfeedc0dedeadbeef)};
+
+static const uint32_t hashtest_key_lens[] = {
1, 2, /* Unusual key sizes */
4, 8, 16, 32, 48, 64, /* standard key sizes */
9, /* IPv4 SRC + DST + protocol, unpadded */
@@ -85,6 +124,9 @@ get_hash_name(rte_hash_function f)
if (f == rte_hash_crc)
return "rte_hash_crc";
+ if (f == rte_hsiphash)
+ return "hsiphash";
+
return "UnknownHash";
}
@@ -126,6 +168,7 @@ run_hash_func_perf_tests(void)
printf(" Number of iterations for each test = %d\n",
HASHTEST_ITERATIONS);
printf("Hash Func. , Key Length (bytes), Initial value, Ticks/Op.\n");
+ fflush(stdout);
for (i = 0; i < RTE_DIM(hashtest_initvals); i++) {
for (j = 0; j < RTE_DIM(hashtest_key_lens); j++) {
@@ -147,13 +190,15 @@ verify_precalculated_hash_func_tests(void)
{
unsigned i, j;
uint8_t key[64];
- uint32_t hash;
for (i = 0; i < 64; i++)
key[i] = (uint8_t) i;
for (i = 0; i < RTE_DIM(hashtest_key_lens); i++) {
for (j = 0; j < RTE_DIM(hashtest_initvals); j++) {
+ uint32_t hash;
+ uint64_t shash;
+
hash = rte_jhash(key, hashtest_key_lens[i],
hashtest_initvals[j]);
if (hash != hash_values_jhash[j][i]) {
@@ -173,6 +218,24 @@ verify_precalculated_hash_func_tests(void)
hash_values_crc[j][i], hash);
return -1;
}
+
+ hash = rte_hsiphash(key, hashtest_key_lens[i], hashtest_initvals[j]);
+ if (hash != hash_values_hsiphash[j][i]) {
+ printf("Hsiphash for %u bytes with initial value 0x%x."
+ "Expected 0x%x, but got 0x%x\n",
+ hashtest_key_lens[i], hashtest_initvals[j],
+ hash_values_hsiphash[j][i], hash);
+ return -1;
+ }
+
+ shash = rte_siphash(key, hashtest_key_lens[i], hashtest_initkeys[j]);
+ if (shash != hash_values_siphash[j][i]) {
+ printf("siphash for %u bytes with initial %#"PRIx64"."
+ "Expected %#"PRIx64", but got %#"PRIx64"\n",
+ hashtest_key_lens[i], hashtest_initkeys[j],
+ hash_values_siphash[j][i], shash);
+ return -1;
+ }
}
}
@@ -6,6 +6,7 @@ headers = files(
'rte_hash_crc.h',
'rte_hash.h',
'rte_jhash.h',
+ 'rte_siphash.h',
'rte_thash.h',
'rte_thash_gfni.h',
)
@@ -21,6 +22,7 @@ sources = files(
'rte_cuckoo_hash.c',
'rte_hash_crc.c',
'rte_fbk_hash.c',
+ 'rte_siphash.c',
'rte_thash.c',
'rte_thash_gfni.c',
)
new file mode 100644
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Based on code reference code licensed as CC0 and MIT.
+ * Copyright (c) 2012-2022 Jean-Philippe Aumasson <jeanphilippe.aumasson@gmail.com>
+ * Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
+ */
+
+#include <stdint.h>
+
+#include <rte_common.h>
+#include "rte_siphash.h"
+
+static __rte_always_inline uint64_t
+rol64(uint64_t x, unsigned int b)
+{
+ return (x << b) | (x >> (64 - b));
+}
+
+#if defined(RTE_ARCH_X86)
+static __rte_always_inline uint64_t u8to64_le(const uint8_t *p)
+{
+ return *(const uint64_t *)p;
+}
+
+static __rte_always_inline uint32_t u8to32_le(const uint8_t *p)
+{
+ return *(const uint32_t *)p;
+}
+
+static __rte_always_inline uint16_t u8to16_le(const uint8_t *p)
+{
+ return *(const uint16_t *)p;
+}
+#else
+/* Portable version */
+static __rte_always_inline uint64_t u8to64_le(const uint8_t *p)
+{
+ return (uint64_t)p[0] | (uint64_t)p[1] << 8 |
+ (uint64_t)p[2] << 16 | (uint64_t)p[3] << 24 |
+ (uint64_t)p[4] << 32 | (uint64_t)p[5] << 40 |
+ (uint64_t)p[6] << 48 | (uint64_t)p[7] << 56);
+}
+
+static __rte_always_inline uint32_t u8to32_le(const void *p)
+{
+ return (uint32_t)p[0] | (uint32_t)p[1] << 8 |
+ (uint32_t)p[2] << 16 | (uint32_t)p[3] << 24;
+}
+
+static __rte_always_inline uint16_t u8to16_le(const uint8_t *p)
+{
+ return (uint16_t)p[0] | (uint16_t)p[1] << 8;
+}
+#endif
+
+#define SIPROUND do { \
+ v0 += v1; \
+ v1 = rol64(v1, 13); \
+ v1 ^= v0; \
+ v0 = rol64(v0, 32); \
+ v2 += v3; \
+ v3 = rol64(v3, 16); \
+ v3 ^= v2; \
+ v0 += v3; \
+ v3 = rol64(v3, 21); \
+ v3 ^= v0; \
+ v2 += v1; \
+ v1 = rol64(v1, 17); \
+ v1 ^= v2; \
+ v2 = rol64(v2, 32); \
+} while (0)
+
+/*
+ * Use a 64bit version of SipHash for both full and
+ * half versions, The difference is the number of rounds.
+ */
+static inline uint64_t
+siphash(const uint8_t *data, uint32_t len, uint64_t key[2],
+ const unsigned int cround, const unsigned int dround)
+{
+ const uint32_t left = len & 7;
+ const uint8_t *end = data + len - left;
+ unsigned int i;
+ uint64_t m;
+ uint64_t v0 = UINT64_C(0x736f6d6570736575);
+ uint64_t v1 = UINT64_C(0x646f72616e646f6d);
+ uint64_t v2 = UINT64_C(0x6c7967656e657261);
+ uint64_t v3 = UINT64_C(0x7465646279746573);
+ uint64_t b = (uint64_t)len << 56;
+
+ v3 ^= key[1];
+ v2 ^= key[0];
+ v1 ^= key[1];
+ v0 ^= key[0];
+
+ for (; data != end; data += 8) {
+ m = u8to64_le(data);
+ v3 ^= m;
+
+ for (i = 0; i < cround; i++)
+ SIPROUND;
+
+ v0 ^= m;
+ }
+
+ switch (left) {
+ case 7:
+ b |= ((uint64_t)end[6]) << 48;
+ /* fallthrough */
+ case 6:
+ b |= ((uint64_t)end[5]) << 40;
+ /* fallthrough */
+ case 5:
+ b |= ((uint64_t)end[4]) << 32;
+ /* fallthrough */
+ case 4:
+ b |= u8to32_le(end);
+ break;
+ case 3:
+ b |= ((uint64_t)end[2]) << 16;
+ /* fallthrough */
+ case 2:
+ b |= u8to16_le(end);
+ break;
+ case 1:
+ b |= data[0];
+ }
+
+ v3 ^= b;
+ for (i = 0; i < cround; i++)
+ SIPROUND;
+
+ v0 ^= b;
+ v2 ^= 0xff;
+
+ for (i = 0; i < dround; i++)
+ SIPROUND;
+
+ return (v0 ^ v1) ^ (v2 ^ v3);
+}
+
+uint64_t
+rte_siphash(const void *data, uint32_t len, uint64_t init_val)
+{
+ uint64_t key[2] = { init_val };
+
+ return siphash(data, len, key, 2, 4);
+}
+
+uint32_t
+rte_hsiphash(const void *data, uint32_t len, uint32_t init_val)
+{
+ uint64_t key[2] = { init_val };
+
+ return siphash(data, len, key, 1, 3);
+}
new file mode 100644
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Based on code reference code licensed as CC0 and MIT.
+ * Copyright (c) 2012-2022 Jean-Philippe Aumasson <jeanphilippe.aumasson@gmail.com>
+ * Copyright (c) 2012-2014 Daniel J. Bernstein <djb@cr.yp.to>
+ */
+
+#ifndef _RTE_SIPHASH_H
+#define _RTE_SIPHASH_H
+
+/**
+ * @file
+ *
+ * SipHash is a family of pseudorandom functions (PRFs) optimized for speed on short messages.
+ *
+ * SipHash was designed in 2012 by Jean-Philippe Aumasson and Daniel J. Bernstein as a defense
+ * against hash-flooding DoS attacks.
+ *
+ * SipHash is simpler and faster on short messages than previous cryptographic algorithms,
+ * such as MACs based on universal hashing.
+ * Competitive in performance with insecure non-cryptographic algorithms.
+ *
+ * Cryptographically secure, with no sign of weakness despite multiple cryptanalysis projects
+ * by leading cryptographers.
+ *
+ * Battle-tested, with successful integration in OSs (Linux kernel, OpenBSD, FreeBSD, FreeRTOS),
+ * languages (Perl, Python, Ruby, etc.), libraries (OpenSSL libcrypto, Sodium, etc.)
+ * and applications (Wireguard, Redis, etc.).
+ *
+ * As a secure pseudorandom function (a.k.a. keyed hash function), SipHash can also be used as
+ * a secure message authentication code (MAC). But SipHash is not a hash in the sense of
+ * general-purpose key-less hash function such as BLAKE3 or SHA-3.
+ * SipHash should therefore always be used with a secret key in order to be secure.
+ * siphash functions.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#include <rte_compat.h>
+
+/**
+ * Compute a SipHash-2-4
+ * uses a 64 bit key; does 2 compression rounds, and 4 finalization rounds.
+ *
+ * @param data
+ * Data to perform hash on.
+ * @param len
+ * How many bytes to use to calculate hash value.
+ * @param initval
+ * Value to initialise hash generator.
+ * @return
+ * 64bit calculated hash value.
+ */
+__rte_experimental
+uint64_t
+rte_siphash(const void *data, uint32_t len, uint64_t init_val);
+
+/**
+ * Compute Siphash-1-3;
+ * uses a 32 bit key; does 1 compression round, and 3 finalization rounds.
+ *
+ * @param data
+ * Data to perform hash on.
+ * @param len
+ * How many bytes to use to calculate hash value.
+ * @param initval
+ * Value to initialise hash generator.
+ * @return
+ * 32bit calculated hash value.
+ */
+__rte_experimental
+uint32_t
+rte_hsiphash(const void *data, uint32_t len, uint32_t init_val);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_SIPHASH_H */
@@ -48,3 +48,10 @@ DPDK_24 {
local: *;
};
+
+EXPERIMENTAL {
+ global:
+
+ rte_siphash;
+ rte_hsiphash;
+};