1 /* Copyright (C) 2007-2016 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 * Generic variable name utility functions
24 */
25
26 #include "suricata-common.h"
27 #include "detect.h"
28 #include "util-hashlist.h"
29 #include "util-var-name.h"
30
31 /* the way this can be used w/o locking lookups:
32 * - Lookups use only g_varnamestore_current which is read only
33 * - Detection setups a new ctx in staging, which will include the 'current'
34 * entries keeping ID's stable
35 * - Detection hot swaps staging into current after a new detect engine was
36 * created. Current remains available through 'old'.
37 * - When detect reload is complete (threads are all moved over), 'old' can
38 * be freed.
39 */
40
41 typedef struct VarNameStore_ {
42 HashListTable *names;
43 HashListTable *ids;
44 uint32_t max_id;
45 uint32_t de_ctx_version; /**< de_ctx version 'owning' this */
46 } VarNameStore;
47
48 static int initialized = 0;
49 /* currently VarNameStore that is READ ONLY. This way lookups can
50 * be done w/o locking or synchronization */
51 SC_ATOMIC_DECLARE(VarNameStore *, g_varnamestore_current);
52
53 /* old VarNameStore on the way out */
54 static VarNameStore *g_varnamestore_old = NULL;
55
56 /* new VarNameStore that is being prepared. Multiple DetectLoader threads
57 * may be updating it so a lock is used for synchronization. */
58 static VarNameStore *g_varnamestore_staging = NULL;
59 static SCMutex g_varnamestore_staging_m = SCMUTEX_INITIALIZER;
60
61 /** \brief Name2idx mapping structure for flowbits, flowvars and pktvars. */
62 typedef struct VariableName_ {
63 char *name;
64 uint8_t type; /* flowbit, pktvar, etc */
65 uint32_t idx;
66 } VariableName;
67
68 #define VARNAME_HASHSIZE 0x1000
69 #define VARID_HASHSIZE 0x1000
70
VariableNameHash(HashListTable * ht,void * buf,uint16_t buflen)71 static uint32_t VariableNameHash(HashListTable *ht, void *buf, uint16_t buflen)
72 {
73 VariableName *fn = (VariableName *)buf;
74 uint32_t hash = strlen(fn->name) + fn->type;
75 uint16_t u;
76
77 for (u = 0; u < buflen; u++) {
78 hash += fn->name[u];
79 }
80
81 return (hash % VARNAME_HASHSIZE);
82 }
83
VariableNameCompare(void * buf1,uint16_t len1,void * buf2,uint16_t len2)84 static char VariableNameCompare(void *buf1, uint16_t len1, void *buf2, uint16_t len2)
85 {
86 VariableName *fn1 = (VariableName *)buf1;
87 VariableName *fn2 = (VariableName *)buf2;
88
89 if (fn1->type != fn2->type)
90 return 0;
91
92 if (strcmp(fn1->name,fn2->name) == 0)
93 return 1;
94
95 return 0;
96 }
97
VariableIdxHash(HashListTable * ht,void * buf,uint16_t buflen)98 static uint32_t VariableIdxHash(HashListTable *ht, void *buf, uint16_t buflen)
99 {
100 VariableName *fn = (VariableName *)buf;
101 uint32_t hash = fn->idx + fn->type;
102 return (hash % VARID_HASHSIZE);
103 }
104
VariableIdxCompare(void * buf1,uint16_t len1,void * buf2,uint16_t len2)105 static char VariableIdxCompare(void *buf1, uint16_t len1, void *buf2, uint16_t len2)
106 {
107 VariableName *fn1 = (VariableName *)buf1;
108 VariableName *fn2 = (VariableName *)buf2;
109
110 if (fn1->type != fn2->type)
111 return 0;
112
113 if (fn1->idx == fn2->idx)
114 return 1;
115
116 return 0;
117 }
118
VariableNameFree(void * data)119 static void VariableNameFree(void *data)
120 {
121 VariableName *fn = (VariableName *)data;
122
123 if (fn == NULL)
124 return;
125
126 if (fn->name != NULL) {
127 SCFree(fn->name);
128 fn->name = NULL;
129 }
130
131 SCFree(fn);
132 }
133
134 /** \brief Initialize the Name idx hash.
135 */
VarNameStoreInit(void)136 static VarNameStore *VarNameStoreInit(void)
137 {
138 VarNameStore *v = SCCalloc(1, sizeof(*v));
139 if (v == NULL)
140 return NULL;
141
142 v->names = HashListTableInit(VARNAME_HASHSIZE, VariableNameHash, VariableNameCompare, VariableNameFree);
143 if (v->names == NULL) {
144 SCFree(v);
145 return NULL;
146 }
147
148 v->ids = HashListTableInit(VARID_HASHSIZE, VariableIdxHash, VariableIdxCompare, NULL);
149 if (v->ids == NULL) {
150 HashListTableFree(v->names);
151 SCFree(v);
152 return NULL;
153 }
154
155 v->max_id = 0;
156 return v;
157 }
158
VarNameStoreDoFree(VarNameStore * v)159 static void VarNameStoreDoFree(VarNameStore *v)
160 {
161 if (v) {
162 HashListTableFree(v->names);
163 HashListTableFree(v->ids);
164 SCFree(v);
165 }
166 }
167
168
169 /** \brief Get a name idx for a name. If the name is already used reuse the idx.
170 * \param name nul terminated string with the name
171 * \param type variable type
172 * \retval 0 in case of error
173 * \retval idx the idx or 0
174 */
VariableNameGetIdx(VarNameStore * v,const char * name,enum VarTypes type)175 static uint32_t VariableNameGetIdx(VarNameStore *v, const char *name, enum VarTypes type)
176 {
177 uint32_t idx = 0;
178
179 VariableName *fn = SCMalloc(sizeof(VariableName));
180 if (unlikely(fn == NULL))
181 goto error;
182
183 memset(fn, 0, sizeof(VariableName));
184
185 fn->type = type;
186 fn->name = SCStrdup(name);
187 if (fn->name == NULL)
188 goto error;
189
190 VariableName *lookup_fn = (VariableName *)HashListTableLookup(v->names, (void *)fn, 0);
191 if (lookup_fn == NULL) {
192 v->max_id++;
193
194 idx = fn->idx = v->max_id;
195 HashListTableAdd(v->names, (void *)fn, 0);
196 HashListTableAdd(v->ids, (void *)fn, 0);
197 SCLogDebug("new registration %s id %u type %u", fn->name, fn->idx, fn->type);
198 } else {
199 idx = lookup_fn->idx;
200 VariableNameFree(fn);
201 }
202
203 return idx;
204 error:
205 VariableNameFree(fn);
206 return 0;
207 }
208
209 /** \brief Get a name from the idx.
210 * \param idx index of the variable whose name is to be fetched
211 * \param type variable type
212 * \retval NULL in case of error
213 * \retval name of the variable if successful.
214 * \todo no alloc on lookup
215 */
VariableIdxGetName(VarNameStore * v,uint32_t idx,enum VarTypes type)216 static char *VariableIdxGetName(VarNameStore *v, uint32_t idx, enum VarTypes type)
217 {
218 VariableName *fn = SCMalloc(sizeof(VariableName));
219 if (unlikely(fn == NULL))
220 goto error;
221
222 char *name = NULL;
223 memset(fn, 0, sizeof(VariableName));
224
225 fn->type = type;
226 fn->idx = idx;
227
228 VariableName *lookup_fn = (VariableName *)HashListTableLookup(v->ids, (void *)fn, 0);
229 if (lookup_fn != NULL) {
230 name = SCStrdup(lookup_fn->name);
231 if (unlikely(name == NULL))
232 goto error;
233
234 VariableNameFree(fn);
235 } else {
236 goto error;
237 }
238
239 return name;
240 error:
241 VariableNameFree(fn);
242 return NULL;
243 }
244
245 /** \brief setup staging store. Include current store if there is one.
246 */
VarNameStoreSetupStaging(uint32_t de_ctx_version)247 int VarNameStoreSetupStaging(uint32_t de_ctx_version)
248 {
249 SCMutexLock(&g_varnamestore_staging_m);
250
251 if (!initialized) {
252 SC_ATOMIC_INITPTR(g_varnamestore_current);
253 initialized = 1;
254 }
255
256 if (g_varnamestore_staging != NULL &&
257 g_varnamestore_staging->de_ctx_version == de_ctx_version) {
258 SCMutexUnlock(&g_varnamestore_staging_m);
259 return 0;
260 }
261
262 VarNameStore *nv = VarNameStoreInit();
263 if (nv == NULL) {
264 SCMutexUnlock(&g_varnamestore_staging_m);
265 return -1;
266 }
267 g_varnamestore_staging = nv;
268 nv->de_ctx_version = de_ctx_version;
269
270 VarNameStore *current = SC_ATOMIC_GET(g_varnamestore_current);
271 if (current) {
272 /* add all entries from the current hash into this new one. */
273 HashListTableBucket *b = HashListTableGetListHead(current->names);
274 while (b) {
275 VariableName *var = HashListTableGetListData(b);
276
277 VariableName *newvar = SCCalloc(1, sizeof(*newvar));
278 BUG_ON(newvar == NULL);
279 memcpy(newvar, var, sizeof(*newvar));
280 newvar->name = SCStrdup(var->name);
281 BUG_ON(newvar->name == NULL);
282
283 HashListTableAdd(nv->names, (void *)newvar, 0);
284 HashListTableAdd(nv->ids, (void *)newvar, 0);
285 nv->max_id = MAX(nv->max_id, newvar->idx);
286 SCLogDebug("xfer %s id %u type %u", newvar->name, newvar->idx, newvar->type);
287
288 b = HashListTableGetListNext(b);
289 }
290 }
291
292 SCLogDebug("set up staging with detect engine ver %u", nv->de_ctx_version);
293 SCMutexUnlock(&g_varnamestore_staging_m);
294 return 0;
295 }
296
VarNameStoreLookupById(const uint32_t id,const enum VarTypes type)297 const char *VarNameStoreLookupById(const uint32_t id, const enum VarTypes type)
298 {
299 VarNameStore *current = SC_ATOMIC_GET(g_varnamestore_current);
300 BUG_ON(current == NULL);
301 VariableName lookup = { NULL, type, id };
302 VariableName *found = (VariableName *)HashListTableLookup(current->ids, (void *)&lookup, 0);
303 if (found == NULL) {
304 return NULL;
305 }
306 return found->name;
307 }
308
VarNameStoreLookupByName(const char * name,const enum VarTypes type)309 uint32_t VarNameStoreLookupByName(const char *name, const enum VarTypes type)
310 {
311 VarNameStore *current = SC_ATOMIC_GET(g_varnamestore_current);
312 BUG_ON(current == NULL);
313 VariableName lookup = { (char *)name, type, 0 };
314 VariableName *found = (VariableName *)HashListTableLookup(current->names, (void *)&lookup, 0);
315 if (found == NULL) {
316 return 0;
317 }
318 SCLogDebug("found %u for %s type %u", found->idx, name, type);
319 return found->idx;
320 }
321
322 /** \brief add to staging or return existing id if already in there */
VarNameStoreSetupAdd(const char * name,const enum VarTypes type)323 uint32_t VarNameStoreSetupAdd(const char *name, const enum VarTypes type)
324 {
325 uint32_t id;
326 SCMutexLock(&g_varnamestore_staging_m);
327 id = VariableNameGetIdx(g_varnamestore_staging, name, type);
328 SCMutexUnlock(&g_varnamestore_staging_m);
329 return id;
330 }
331
VarNameStoreSetupLookup(uint32_t idx,const enum VarTypes type)332 char *VarNameStoreSetupLookup(uint32_t idx, const enum VarTypes type)
333 {
334 SCMutexLock(&g_varnamestore_staging_m);
335 char *name = VariableIdxGetName(g_varnamestore_staging, idx, type);
336 SCMutexUnlock(&g_varnamestore_staging_m);
337 return name;
338 }
339
VarNameStoreActivateStaging(void)340 void VarNameStoreActivateStaging(void)
341 {
342 SCMutexLock(&g_varnamestore_staging_m);
343 if (g_varnamestore_old) {
344 VarNameStoreDoFree(g_varnamestore_old);
345 g_varnamestore_old = NULL;
346 }
347 g_varnamestore_old = SC_ATOMIC_GET(g_varnamestore_current);
348 SC_ATOMIC_SET(g_varnamestore_current, g_varnamestore_staging);
349 g_varnamestore_staging = NULL;
350 SCMutexUnlock(&g_varnamestore_staging_m);
351 }
352
VarNameStoreFreeOld(void)353 void VarNameStoreFreeOld(void)
354 {
355 SCMutexLock(&g_varnamestore_staging_m);
356 SCLogDebug("freeing g_varnamestore_old %p", g_varnamestore_old);
357 if (g_varnamestore_old) {
358 VarNameStoreDoFree(g_varnamestore_old);
359 g_varnamestore_old = NULL;
360 }
361 SCMutexUnlock(&g_varnamestore_staging_m);
362 }
363
VarNameStoreFree(uint32_t de_ctx_version)364 void VarNameStoreFree(uint32_t de_ctx_version)
365 {
366 SCLogDebug("freeing detect engine version %u", de_ctx_version);
367 SCMutexLock(&g_varnamestore_staging_m);
368 if (g_varnamestore_old && g_varnamestore_old->de_ctx_version == de_ctx_version) {
369 VarNameStoreDoFree(g_varnamestore_old);
370 g_varnamestore_old = NULL;
371 SCLogDebug("freeing detect engine version %u: old done", de_ctx_version);
372 }
373
374 /* if at this point we have a staging area which matches our version
375 * we didn't complete the setup and are cleaning up the mess. */
376 if (g_varnamestore_staging && g_varnamestore_staging->de_ctx_version == de_ctx_version) {
377 VarNameStoreDoFree(g_varnamestore_staging);
378 g_varnamestore_staging = NULL;
379 SCLogDebug("freeing detect engine version %u: staging done", de_ctx_version);
380 }
381
382 VarNameStore *current = SC_ATOMIC_GET(g_varnamestore_current);
383 if (current && current->de_ctx_version == de_ctx_version) {
384 VarNameStoreDoFree(current);
385 SC_ATOMIC_SET(g_varnamestore_current, NULL);
386 SCLogDebug("freeing detect engine version %u: current done", de_ctx_version);
387 }
388 SCMutexUnlock(&g_varnamestore_staging_m);
389 }
390