suricata
util-rohash.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2024 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Victor Julien <victor@inliniac.net>
22  *
23  * Chained read only hash table implementation, meaning that
24  * after the initial fill no changes are allowed.
25  *
26  * Loading takes 2 stages.
27  * - stage1 maps data
28  * - stage2 fills blob
29  *
30  * \todo a bloomfilter in the ROHashTableOffsets could possibly prevent
31  * a lot of cache misses when validating a potential match
32  *
33  * \todo maybe add a user ctx to be returned instead, something like a
34  * 4/8 byte ptr or simply a flag
35  */
36 
37 #include "suricata-common.h"
38 #include "util-hash.h"
39 #include "util-memcmp.h"
40 #include "util-hash-lookup3.h"
41 #include "util-rohash.h"
42 #include "util-debug.h"
43 
44 /** item_size data beyond this header */
45 typedef struct ROHashTableItem_ {
46  uint32_t pos; /**< position relative to other values with same hash */
49 
50 /** offset table */
51 typedef struct ROHashTableOffsets_ {
52  uint32_t cnt; /**< number of items for this hash */
53  uint32_t offset; /**< position in the blob of the first item */
55 
56 /** \brief initialize a new rohash
57  *
58  * \param hash_bits hash size as 2^hash_bits, so power of 2, max 31
59  * \param item_size size of the data to store
60  *
61  * \retval table ptr or NULL on error
62  */
63 ROHashTable *ROHashInit(uint8_t hash_bits, uint16_t item_size)
64 {
65  if (item_size % 4 != 0 || item_size == 0) {
66  SCLogError("data size must be multiple of 4");
67  return NULL;
68  }
69  if (hash_bits < 4 || hash_bits > 31) {
70  SCLogError("invalid hash_bits setting, valid range is 4-31");
71  return NULL;
72  }
73 
74  uint32_t size = hashsize(hash_bits) * sizeof(ROHashTableOffsets);
75 
76  ROHashTable *table = SCCalloc(1, sizeof(ROHashTable) + size);
77  if (unlikely(table == NULL)) {
78  SCLogError("failed to alloc memory");
79  return NULL;
80  }
81 
82  table->items = 0;
83  table->item_size = item_size;
84  table->hash_bits = hash_bits;
85  TAILQ_INIT(&table->head);
86 
87  return table;
88 }
89 
90 void ROHashFree(ROHashTable *table)
91 {
92  if (table != NULL) {
93  if (table->data != NULL) {
94  SCFree(table->data);
95  }
96 
97  SCFree(table);
98  }
99 }
100 
102 {
103  uint32_t r1 = hashsize(table->hash_bits) * sizeof(ROHashTableOffsets);
104  uint32_t r2 = table->items * table->item_size;
105  return (uint32_t)(r1 + r2 + sizeof(ROHashTable));
106 }
107 
108 /**
109  * \retval NULL not found
110  * \retval ptr found
111  */
112 void *ROHashLookup(ROHashTable *table, void *data, uint16_t size)
113 {
114  if (data == NULL || size != table->item_size) {
115  SCReturnPtr(NULL, "void");
116  }
117 
118  const uint32_t hash = hashword(data, table->item_size / 4, 0) & hashmask(table->hash_bits);
119 
120  /* get offsets start */
121  const ROHashTableOffsets *os = (ROHashTableOffsets *)((uint8_t *)table + sizeof(ROHashTable));
122  const ROHashTableOffsets *o = &os[hash];
123 
124  /* no matches */
125  if (o->cnt == 0) {
126  SCReturnPtr(NULL, "void");
127  }
128 
129  for (uint32_t u = 0; u < o->cnt; u++) {
130  uint32_t offset = (o->offset + u) * table->item_size;
131 
132  if (SCMemcmp(table->data + offset, data, table->item_size) == 0) {
133  SCReturnPtr(table->data + offset, "void");
134  }
135  }
136  SCReturnPtr(NULL, "void");
137 }
138 
139 /** \brief Add a new value to the hash
140  *
141  * \note can only be done when table isn't in a locked state yet
142  *
143  * \param table the hash table
144  * \param value value to add
145  * \param size value size. *MUST* match table item_size
146  *
147  * \retval 0 error
148  * \retval 1 ok
149  */
150 int ROHashInitQueueValue(ROHashTable *table, void *value, uint16_t size)
151 {
152  if (table->locked) {
153  SCLogError("can't add value to locked table");
154  return 0;
155  }
156  if (table->item_size != size) {
157  SCLogError("wrong size for data %u != %u", size, table->item_size);
158  return 0;
159  }
160 
161  ROHashTableItem *item = SCCalloc(1, sizeof(ROHashTableItem) + table->item_size);
162  if (item != NULL) {
163  memcpy((void *)item + sizeof(ROHashTableItem), value, table->item_size);
164  TAILQ_INSERT_TAIL(&table->head, item, next);
165  return 1;
166  }
167 
168  return 0;
169 }
170 
171 /** \brief create final hash data structure
172  *
173  * \param table the hash table
174  *
175  * \retval 0 error
176  * \retval 1 ok
177  *
178  * \note after this call the nothing can be added to the hash anymore.
179  */
181 {
182  if (table->locked) {
183  SCLogError("table already locked");
184  return 0;
185  }
186 
187  ROHashTableItem *item = NULL;
188  ROHashTableOffsets *os = (ROHashTableOffsets *)((uint8_t *)table + sizeof(ROHashTable));
189 
190  /* count items per hash value */
191  TAILQ_FOREACH(item, &table->head, next) {
192  uint32_t hash =
193  hashword((uint32_t *)((uint8_t *)item + sizeof(*item)), table->item_size / 4, 0) &
194  hashmask(table->hash_bits);
195  ROHashTableOffsets *o = &os[hash];
196 
197  item->pos = o->cnt;
198  o->cnt++;
199  table->items++;
200  }
201 
202  if (table->items == 0) {
203  SCLogError("no items");
204  return 0;
205  }
206 
207  /* get the data block */
208  uint32_t newsize = table->items * table->item_size;
209  table->data = SCCalloc(1, newsize);
210  if (table->data == NULL) {
211  SCLogError("failed to alloc memory");
212  return 0;
213  }
214 
215  /* calc offsets into the block per hash value */
216  uint32_t total = 0;
217  for (uint32_t x = 0; x < hashsize(table->hash_bits); x++) {
218  ROHashTableOffsets *o = &os[x];
219 
220  if (o->cnt == 0)
221  continue;
222 
223  o->offset = total;
224  total += o->cnt;
225  }
226 
227  /* copy each value into the data block */
228  TAILQ_FOREACH(item, &table->head, next) {
229  uint32_t hash =
230  hashword((uint32_t *)((uint8_t *)item + sizeof(*item)), table->item_size / 4, 0) &
231  hashmask(table->hash_bits);
232 
233  ROHashTableOffsets *o = &os[hash];
234  uint32_t offset = (o->offset + item->pos) * table->item_size;
235 
236  memcpy(table->data + offset, (uint8_t *)item + sizeof(*item), table->item_size);
237  }
238 
239  /* clean up temp items */
240  while ((item = TAILQ_FIRST(&table->head))) {
241  TAILQ_REMOVE(&table->head, item, next);
242  SCFree(item);
243  }
244 
245  table->locked = 1;
246  return 1;
247 }
ROHashTable_::data
void * data
Definition: util-rohash.h:34
hashword
uint32_t hashword(const uint32_t *k, size_t length, uint32_t initval)
Definition: util-hash-lookup3.c:172
offset
uint64_t offset
Definition: util-streaming-buffer.h:0
TAILQ_INIT
#define TAILQ_INIT(head)
Definition: queue.h:262
ROHashTableOffsets
struct ROHashTableOffsets_ ROHashTableOffsets
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
ROHashTableOffsets_
Definition: util-rohash.c:51
ROHashTableOffsets_::offset
uint32_t offset
Definition: util-rohash.c:53
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
util-hash.h
TAILQ_FOREACH
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:252
ROHashTable_
Definition: util-rohash.h:29
TAILQ_INSERT_TAIL
#define TAILQ_INSERT_TAIL(head, elm, field)
Definition: queue.h:294
util-rohash.h
TAILQ_ENTRY
#define TAILQ_ENTRY(type)
Definition: queue.h:239
util-memcmp.h
ROHashInitFinalize
int ROHashInitFinalize(ROHashTable *table)
create final hash data structure
Definition: util-rohash.c:180
TAILQ_REMOVE
#define TAILQ_REMOVE(head, elm, field)
Definition: queue.h:312
util-debug.h
TAILQ_FIRST
#define TAILQ_FIRST(head)
Definition: queue.h:250
ROHashTable_::item_size
uint16_t item_size
Definition: util-rohash.h:32
ROHashLookup
void * ROHashLookup(ROHashTable *table, void *data, uint16_t size)
Definition: util-rohash.c:112
ROHashTableOffsets_::cnt
uint32_t cnt
Definition: util-rohash.c:52
ROHashTable_::hash_bits
uint8_t hash_bits
Definition: util-rohash.h:31
ROHashMemorySize
uint32_t ROHashMemorySize(ROHashTable *table)
Definition: util-rohash.c:101
SCReturnPtr
#define SCReturnPtr(x, type)
Definition: util-debug.h:300
ROHashTableItem_
Definition: util-rohash.c:45
ROHashTable_::items
uint32_t items
Definition: util-rohash.h:33
suricata-common.h
ROHashInit
ROHashTable * ROHashInit(uint8_t hash_bits, uint16_t item_size)
initialize a new rohash
Definition: util-rohash.c:63
hashsize
#define hashsize(n)
Definition: util-hash-lookup3.h:40
ROHashInitQueueValue
int ROHashInitQueueValue(ROHashTable *table, void *value, uint16_t size)
Add a new value to the hash.
Definition: util-rohash.c:150
util-hash-lookup3.h
hashmask
#define hashmask(n)
Definition: util-hash-lookup3.h:41
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:274
ROHashFree
void ROHashFree(ROHashTable *table)
Definition: util-rohash.c:90
SCFree
#define SCFree(p)
Definition: util-mem.h:61
ROHashTable_::locked
uint8_t locked
Definition: util-rohash.h:30
ROHashTable
struct ROHashTable_ ROHashTable
ROHashTableItem_::pos
uint32_t pos
Definition: util-rohash.c:46
ROHashTableItem
struct ROHashTableItem_ ROHashTableItem
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCMemcmp
#define SCMemcmp(a, b, c)
Definition: util-memcmp.h:289