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