1 /*
2 ** fbinfomodel.c
3 ** IPFIX Information Model and IE storage management
4 **
5 ** ------------------------------------------------------------------------
6 ** Copyright (C) 2006-2019 Carnegie Mellon University. All Rights Reserved.
7 ** ------------------------------------------------------------------------
8 ** Authors: Brian Trammell
9 ** ------------------------------------------------------------------------
10 ** @OPENSOURCE_LICENSE_START@
11 ** libfixbuf 2.0
12 **
13 ** Copyright 2018-2019 Carnegie Mellon University. All Rights Reserved.
14 **
15 ** NO WARRANTY. THIS CARNEGIE MELLON UNIVERSITY AND SOFTWARE
16 ** ENGINEERING INSTITUTE MATERIAL IS FURNISHED ON AN "AS-IS"
17 ** BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND,
18 ** EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT
19 ** LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY,
20 ** EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE
21 ** MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF
22 ** ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR
23 ** COPYRIGHT INFRINGEMENT.
24 **
25 ** Released under a GNU-Lesser GPL 3.0-style license, please see
26 ** LICENSE.txt or contact permission@sei.cmu.edu for full terms.
27 **
28 ** [DISTRIBUTION STATEMENT A] This material has been approved for
29 ** public release and unlimited distribution. Please see Copyright
30 ** notice for non-US Government use and distribution.
31 **
32 ** Carnegie Mellon(R) and CERT(R) are registered in the U.S. Patent
33 ** and Trademark Office by Carnegie Mellon University.
34 **
35 ** DM18-0325
36 ** @OPENSOURCE_LICENSE_END@
37 ** ------------------------------------------------------------------------
38 */
39
40 #define _FIXBUF_SOURCE_
41 #include <fixbuf/private.h>
42
43
44 #define CERT_PEN 6871
45
46 struct fbInfoModel_st {
47 GHashTable *ie_table;
48 GHashTable *ie_byname;
49 GStringChunk *ie_names;
50 GStringChunk *ie_desc;
51 };
52
53
54 #include "infomodel.h"
55
56
57 static fbInfoElementSpec_t ie_type_spec[] = {
58 {"privateEnterpriseNumber", 4, 0 },
59 {"informationElementId", 2, 0 },
60 {"informationElementDataType", 1, 0 },
61 {"informationElementSemantics", 1, 0 },
62 {"informationElementUnits", 2, 0 },
63 {"paddingOctets", 6, 1 },
64 {"informationElementRangeBegin", 8, 0 },
65 {"informationElementRangeEnd", 8, 0 },
66 {"informationElementName", FB_IE_VARLEN, 0 },
67 {"informationElementDescription", FB_IE_VARLEN, 0 },
68 FB_IESPEC_NULL
69 };
70
71
fbInfoElementHash(fbInfoElement_t * ie)72 uint32_t fbInfoElementHash(
73 fbInfoElement_t *ie)
74 {
75 return ((ie->ent & 0x0000ffff) << 16) | (ie->num << 2) | (ie->midx << 4);
76 }
77
fbInfoElementEqual(const fbInfoElement_t * a,const fbInfoElement_t * b)78 gboolean fbInfoElementEqual(
79 const fbInfoElement_t *a,
80 const fbInfoElement_t *b)
81 {
82 return ((a->ent == b->ent) && (a->num == b->num) && (a->midx == b->midx));
83 }
84
fbInfoElementDebug(gboolean tmpl,fbInfoElement_t * ie)85 void fbInfoElementDebug(
86 gboolean tmpl,
87 fbInfoElement_t *ie)
88 {
89 if (ie->len == FB_IE_VARLEN) {
90 fprintf(stderr, "VL %02x %08x:%04x %2u (%s)\n",
91 ie->flags, ie->ent, ie->num, ie->midx,
92 tmpl ? ie->ref.canon->ref.name : ie->ref.name);
93 } else {
94 fprintf(stderr, "%2u %02x %08x:%04x %2u (%s)\n",
95 ie->len, ie->flags, ie->ent, ie->num, ie->midx,
96 tmpl ? ie->ref.canon->ref.name : ie->ref.name);
97 }
98 }
99
fbInfoElementFree(fbInfoElement_t * ie)100 static void fbInfoElementFree(
101 fbInfoElement_t *ie)
102 {
103 g_slice_free(fbInfoElement_t, ie);
104 }
105
fbInfoModelAlloc(void)106 fbInfoModel_t *fbInfoModelAlloc(
107 void)
108 {
109 fbInfoModel_t *model = NULL;
110
111 /* Create an information model */
112 model = g_slice_new0(fbInfoModel_t);
113
114 /* Allocate information element tables */
115 model->ie_table = g_hash_table_new_full(
116 (GHashFunc)fbInfoElementHash, (GEqualFunc)fbInfoElementEqual,
117 NULL, (GDestroyNotify)fbInfoElementFree);
118
119 model->ie_byname = g_hash_table_new(g_str_hash, g_str_equal);
120
121 /* Allocate information element name chunk */
122 model->ie_names = g_string_chunk_new(64);
123 model->ie_desc = g_string_chunk_new(128);
124
125 /* Add elements to the information model */
126 infomodelAddGlobalElements(model);
127
128 /* Return the new information model */
129 return model;
130 }
131
fbInfoModelFree(fbInfoModel_t * model)132 void fbInfoModelFree(
133 fbInfoModel_t *model)
134 {
135 if (NULL == model) {
136 return;
137 }
138 g_hash_table_destroy(model->ie_byname);
139 g_string_chunk_free(model->ie_names);
140 g_string_chunk_free(model->ie_desc);
141 g_hash_table_destroy(model->ie_table);
142 g_slice_free(fbInfoModel_t, model);
143 }
144
fbInfoModelReversifyName(const char * fwdname,char * revname,size_t revname_sz)145 static void fbInfoModelReversifyName(
146 const char *fwdname,
147 char *revname,
148 size_t revname_sz)
149 {
150 /* paranoid string copy */
151 strncpy(revname + FB_IE_REVERSE_STRLEN, fwdname, revname_sz - FB_IE_REVERSE_STRLEN - 1);
152 revname[revname_sz - 1] = (char)0;
153
154 /* uppercase first char */
155 revname[FB_IE_REVERSE_STRLEN] = toupper(revname[FB_IE_REVERSE_STRLEN]);
156
157 /* prepend reverse */
158 memcpy(revname, FB_IE_REVERSE_STR, FB_IE_REVERSE_STRLEN);
159 }
160
161 #define FB_IE_REVERSE_BUFSZ 256
162
fbInfoModelAddElement(fbInfoModel_t * model,fbInfoElement_t * ie)163 void fbInfoModelAddElement(
164 fbInfoModel_t *model,
165 fbInfoElement_t *ie)
166 {
167 fbInfoElement_t *model_ie = NULL;
168 fbInfoElement_t *found;
169 char revname[FB_IE_REVERSE_BUFSZ];
170
171 g_assert(ie);
172
173 /* Allocate a new information element */
174 model_ie = g_slice_new0(fbInfoElement_t);
175
176 /* Copy external IE to model IE */
177
178 model_ie->ref.name = g_string_chunk_insert(model->ie_names, ie->ref.name);
179 model_ie->midx = 0;
180 model_ie->ent = ie->ent;
181 model_ie->num = ie->num;
182 model_ie->len = ie->len;
183 model_ie->flags = ie->flags;
184 model_ie->min = ie->min;
185 model_ie->max = ie->max;
186 model_ie->type = ie->type;
187 if (ie->description) {
188 model_ie->description = g_string_chunk_insert(model->ie_desc,
189 ie->description);
190 }
191
192 /* Insert model IE into tables */
193 if ((found = g_hash_table_lookup(model->ie_table, model_ie))) {
194 /* use g_hash_table_replace() if the ent/num already exists.
195 * insert() will replace but it only replaces the value, not
196 * the key. since insert() frees the value and the key and
197 * the value are the same - this creates a problem. replace()
198 * replaces the key and the value. */
199
200 /* since it is possible that 'found' has a different name than
201 * 'model_ie', we need to remove 'found' from the ie_byname
202 * table to avoid having a reference to freed memory. it's
203 * also possible that 'found' is only in the ie_table. */
204 if (g_hash_table_lookup(model->ie_byname, found->ref.name) == found) {
205 g_hash_table_remove(model->ie_byname, found->ref.name);
206 }
207 g_hash_table_replace(model->ie_table, model_ie, model_ie);
208 } else {
209 g_hash_table_insert(model->ie_table, model_ie, model_ie);
210 }
211
212 g_hash_table_insert(model->ie_byname, (char *)model_ie->ref.name,model_ie);
213
214 /* Short circuit if not reversible */
215 if (!(ie->flags & FB_IE_F_REVERSIBLE)) {
216 return;
217 }
218
219 /* Allocate a new reverse information element */
220 model_ie = g_slice_new0(fbInfoElement_t);
221
222 /* Generate reverse name */
223 fbInfoModelReversifyName(ie->ref.name, revname, sizeof(revname));
224
225 /* Copy external IE to reverse model IE */
226 model_ie->ref.name = g_string_chunk_insert(model->ie_names, revname);
227 model_ie->midx = 0;
228 model_ie->ent = ie->ent ? ie->ent : FB_IE_PEN_REVERSE;
229 model_ie->num = ie->ent ? ie->num | FB_IE_VENDOR_BIT_REVERSE : ie->num;
230 model_ie->len = ie->len;
231 model_ie->flags = ie->flags;
232 model_ie->min = ie->min;
233 model_ie->max = ie->max;
234 model_ie->type = ie->type;
235
236 /* Insert model IE into tables */
237 if ((found = g_hash_table_lookup(model->ie_table, model_ie))) {
238 if (g_hash_table_lookup(model->ie_byname, found->ref.name) == found) {
239 g_hash_table_remove(model->ie_byname, found->ref.name);
240 }
241 g_hash_table_replace(model->ie_table, model_ie, model_ie);
242 } else {
243 g_hash_table_insert(model->ie_table, model_ie, model_ie);
244 }
245 g_hash_table_insert(model->ie_byname, (char *)model_ie->ref.name,model_ie);
246 }
247
fbInfoModelAddElementArray(fbInfoModel_t * model,fbInfoElement_t * ie)248 void fbInfoModelAddElementArray(
249 fbInfoModel_t *model,
250 fbInfoElement_t *ie)
251 {
252 g_assert(ie);
253 for (; ie->ref.name; ie++) fbInfoModelAddElement(model, ie);
254 }
255
fbInfoModelGetElement(fbInfoModel_t * model,fbInfoElement_t * ex_ie)256 const fbInfoElement_t *fbInfoModelGetElement(
257 fbInfoModel_t *model,
258 fbInfoElement_t *ex_ie)
259 {
260 return g_hash_table_lookup(model->ie_table, ex_ie);
261 }
262
fbInfoElementCopyToTemplate(fbInfoModel_t * model,fbInfoElement_t * ex_ie,fbInfoElement_t * tmpl_ie)263 gboolean fbInfoElementCopyToTemplate(
264 fbInfoModel_t *model,
265 fbInfoElement_t *ex_ie,
266 fbInfoElement_t *tmpl_ie)
267 {
268 const fbInfoElement_t *model_ie = NULL;
269
270 /* Look up information element in the model */
271 model_ie = fbInfoModelGetElement(model, ex_ie);
272 if (!model_ie) {
273 /* Information element not in model. Note it's alien and add it. */
274 ex_ie->ref.name = g_string_chunk_insert(model->ie_names,
275 "_alienInformationElement");
276 ex_ie->flags |= FB_IE_F_ALIEN;
277 fbInfoModelAddElement(model, ex_ie);
278 model_ie = fbInfoModelGetElement(model, ex_ie);
279 g_assert(model_ie);
280 }
281
282 /* Refer to canonical IE in the model */
283 tmpl_ie->ref.canon = model_ie;
284
285 /* Copy model IE to template IE */
286 tmpl_ie->midx = 0;
287 tmpl_ie->ent = model_ie->ent;
288 tmpl_ie->num = model_ie->num;
289 tmpl_ie->len = ex_ie->len;
290 tmpl_ie->flags = model_ie->flags;
291 tmpl_ie->type = model_ie->type;
292 tmpl_ie->min = model_ie->min;
293 tmpl_ie->max = model_ie->max;
294 tmpl_ie->description = model_ie->description;
295
296 /* All done */
297 return TRUE;
298 }
299
fbInfoModelGetElementByName(fbInfoModel_t * model,const char * name)300 const fbInfoElement_t *fbInfoModelGetElementByName(
301 fbInfoModel_t *model,
302 const char *name)
303 {
304 g_assert(name);
305 return g_hash_table_lookup(model->ie_byname, name);
306 }
307
fbInfoModelGetElementByID(fbInfoModel_t * model,uint16_t id,uint32_t ent)308 const fbInfoElement_t *fbInfoModelGetElementByID(
309 fbInfoModel_t *model,
310 uint16_t id,
311 uint32_t ent)
312 {
313 fbInfoElement_t tempElement;
314
315 tempElement.midx = 0;
316 tempElement.ent = ent;
317 tempElement.num = id;
318
319 return fbInfoModelGetElement(model, &tempElement);
320 }
321
fbInfoElementCopyToTemplateByName(fbInfoModel_t * model,const char * name,uint16_t len_override,fbInfoElement_t * tmpl_ie)322 gboolean fbInfoElementCopyToTemplateByName(
323 fbInfoModel_t *model,
324 const char *name,
325 uint16_t len_override,
326 fbInfoElement_t *tmpl_ie)
327 {
328 const fbInfoElement_t *model_ie = NULL;
329
330 /* Look up information element in the model */
331 model_ie = fbInfoModelGetElementByName(model, name);
332 if (!model_ie) return FALSE;
333
334 /* Refer to canonical IE in the model */
335 tmpl_ie->ref.canon = model_ie;
336
337 /* Copy model IE to template IE */
338 tmpl_ie->midx = 0;
339 tmpl_ie->ent = model_ie->ent;
340 tmpl_ie->num = model_ie->num;
341 tmpl_ie->len = len_override ? len_override : model_ie->len;
342 tmpl_ie->flags = model_ie->flags;
343 tmpl_ie->type = model_ie->type;
344 tmpl_ie->min = model_ie->min;
345 tmpl_ie->max = model_ie->max;
346 tmpl_ie->description = model_ie->description;
347
348 /* All done */
349 return TRUE;
350 }
351
fbInfoElementAllocTypeTemplate(fbInfoModel_t * model,GError ** err)352 fbTemplate_t *fbInfoElementAllocTypeTemplate(
353 fbInfoModel_t *model,
354 GError **err)
355 {
356 return fbInfoElementAllocTypeTemplate2(model, TRUE, err);
357 }
358
fbInfoElementAllocTypeTemplate2(fbInfoModel_t * model,gboolean internal,GError ** err)359 fbTemplate_t *fbInfoElementAllocTypeTemplate2(
360 fbInfoModel_t *model,
361 gboolean internal,
362 GError **err)
363 {
364 fbTemplate_t *tmpl;
365 uint32_t flags;
366
367 flags = internal ? ~0 : 0;
368
369 tmpl = fbTemplateAlloc(model);
370 if (!fbTemplateAppendSpecArray(tmpl, ie_type_spec, flags, err)) {
371 fbTemplateFreeUnused(tmpl);
372 return NULL;
373 }
374 fbTemplateSetOptionsScope(tmpl, 2);
375 return tmpl;
376 }
377
fbInfoElementWriteOptionsRecord(fBuf_t * fbuf,const fbInfoElement_t * model_ie,uint16_t itid,uint16_t etid,GError ** err)378 gboolean fbInfoElementWriteOptionsRecord(
379 fBuf_t *fbuf,
380 const fbInfoElement_t *model_ie,
381 uint16_t itid,
382 uint16_t etid,
383 GError **err)
384 {
385 fbInfoElementOptRec_t rec;
386
387 g_assert(model_ie);
388 if (model_ie == NULL) {
389 g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_NOELEMENT,
390 "Invalid [NULL] Information Element");
391 return FALSE;
392 }
393
394 rec.ie_range_begin = model_ie->min;
395 rec.ie_range_end = model_ie->max;
396 rec.ie_pen = model_ie->ent;
397 rec.ie_units = FB_IE_UNITS(model_ie->flags);
398 rec.ie_semantic = FB_IE_SEMANTIC(model_ie->flags);
399 rec.ie_id = model_ie->num;
400 rec.ie_type = model_ie->type;
401 memset(rec.padding, 0, sizeof(rec.padding));
402 rec.ie_name.buf = (uint8_t *)model_ie->ref.name;
403 rec.ie_name.len = strlen(model_ie->ref.name);
404 rec.ie_desc.buf = (uint8_t *)model_ie->description;
405 if (model_ie->description) {
406 rec.ie_desc.len = strlen(model_ie->description);
407 } else {
408 rec.ie_desc.len = 0;
409 }
410
411 if (!fBufSetExportTemplate(fbuf, etid, err)) {
412 return FALSE;
413 }
414
415 if (!fBufSetInternalTemplate(fbuf, itid, err)) {
416 return FALSE;
417 }
418
419 if (!fBufAppend(fbuf, (uint8_t *)&rec, sizeof(rec), err)) {
420 return FALSE;
421 }
422
423 return TRUE;
424 }
425
fbInfoElementAddOptRecElement(fbInfoModel_t * model,fbInfoElementOptRec_t * rec)426 gboolean fbInfoElementAddOptRecElement(
427 fbInfoModel_t *model,
428 fbInfoElementOptRec_t *rec)
429 {
430 fbInfoElement_t ie;
431 char name[500];
432 char description[4096];
433 size_t len;
434
435 if (rec->ie_pen != 0) {
436
437 ie.min = rec->ie_range_begin;
438 ie.max = rec->ie_range_end;
439 ie.ent = rec->ie_pen;
440 ie.num = rec->ie_id;
441 ie.type = rec->ie_type;
442 len = ((rec->ie_name.len < sizeof(name))
443 ? rec->ie_name.len : (sizeof(name) - 1));
444 strncpy(name, (char *)rec->ie_name.buf, len);
445 name[len] = '\0';
446 ie.ref.name = name;
447 len = ((rec->ie_desc.len < sizeof(description))
448 ? rec->ie_desc.len : (sizeof(description) - 1));
449 strncpy(description, (char *)rec->ie_desc.buf, len);
450 description[len] = '\0';
451 ie.description = description;
452 ie.flags = 0;
453 ie.flags |= rec->ie_units << 16;
454 ie.flags |= rec->ie_semantic << 8;
455
456 /* length is inferred from data type */
457 switch (ie.type) {
458 case FB_OCTET_ARRAY:
459 case FB_STRING:
460 case FB_BASIC_LIST:
461 case FB_SUB_TMPL_LIST:
462 case FB_SUB_TMPL_MULTI_LIST:
463 ie.len = FB_IE_VARLEN;
464 break;
465 case FB_UINT_8:
466 case FB_INT_8:
467 case FB_BOOL:
468 ie.len = 1;
469 break;
470 case FB_UINT_16:
471 case FB_INT_16:
472 ie.len = 2;
473 ie.flags |= FB_IE_F_ENDIAN;
474 break;
475 case FB_UINT_32:
476 case FB_INT_32:
477 case FB_DT_SEC:
478 case FB_FLOAT_32:
479 case FB_IP4_ADDR:
480 ie.len = 4;
481 ie.flags |= FB_IE_F_ENDIAN;
482 break;
483 case FB_MAC_ADDR:
484 ie.len = 6;
485 break;
486 case FB_UINT_64:
487 case FB_INT_64:
488 case FB_DT_MILSEC:
489 case FB_DT_MICROSEC:
490 case FB_DT_NANOSEC:
491 case FB_FLOAT_64:
492 ie.len = 8;
493 ie.flags |= FB_IE_F_ENDIAN;
494 break;
495 case FB_IP6_ADDR:
496 ie.len = 16;
497 break;
498 default:
499 g_warning("Adding element %s with invalid data type [%d]", name,
500 rec->ie_type);
501 ie.len = FB_IE_VARLEN;
502 }
503
504 fbInfoModelAddElement(model, &ie);
505 return TRUE;
506 }
507
508 return FALSE;
509 }
510
fbInfoModelTypeInfoRecord(fbTemplate_t * tmpl)511 gboolean fbInfoModelTypeInfoRecord(
512 fbTemplate_t *tmpl)
513 {
514 /* ignore padding. */
515 if (fbTemplateContainsAllFlaggedElementsByName(tmpl, ie_type_spec, 0)) {
516 return TRUE;
517 }
518
519 return FALSE;
520 }
521
fbInfoModelCountElements(const fbInfoModel_t * model)522 guint fbInfoModelCountElements(
523 const fbInfoModel_t *model)
524 {
525 return g_hash_table_size(model->ie_table);
526 }
527
fbInfoModelIterInit(fbInfoModelIter_t * iter,const fbInfoModel_t * model)528 void fbInfoModelIterInit(
529 fbInfoModelIter_t *iter,
530 const fbInfoModel_t *model)
531 {
532 g_assert(iter);
533 g_hash_table_iter_init(iter, model->ie_table);
534 }
535
fbInfoModelIterNext(fbInfoModelIter_t * iter)536 const fbInfoElement_t *fbInfoModelIterNext(
537 fbInfoModelIter_t *iter)
538 {
539 const fbInfoElement_t *ie;
540 g_assert(iter);
541 if (g_hash_table_iter_next(iter, NULL, (gpointer *)&ie)) {
542 return ie;
543 }
544 return NULL;
545 }
546
fbInfoModelAddAlienElement(fbInfoModel_t * model,fbInfoElement_t * ex_ie)547 const fbInfoElement_t *fbInfoModelAddAlienElement(
548 fbInfoModel_t *model,
549 fbInfoElement_t *ex_ie)
550 {
551 const fbInfoElement_t *model_ie = NULL;
552
553 if (ex_ie == NULL) {
554 return NULL;
555 }
556 /* Information element not in model. Note it's alien and add it. */
557 ex_ie->ref.name = g_string_chunk_insert(model->ie_names,
558 "_alienInformationElement");
559 ex_ie->flags |= FB_IE_F_ALIEN;
560 fbInfoModelAddElement(model, ex_ie);
561 model_ie = fbInfoModelGetElement(model, ex_ie);
562 g_assert(model_ie);
563
564 return model_ie;
565 }
566