1 /*
2  ** fbtemplate.c
3  ** IPFIX Template implementation
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 #define DEFINE_TEMPLATE_METADATA_SPEC
42 #include <fixbuf/private.h>
43 
44 
45 
fbTemplateDebug(const char * label,uint16_t tid,fbTemplate_t * tmpl)46 void fbTemplateDebug(
47     const char      *label,
48     uint16_t        tid,
49     fbTemplate_t    *tmpl)
50 {
51     int i;
52 
53     fprintf(stderr, "%s template %04x [%p] iec=%u sc=%u len=%u\n", label, tid,
54             (void *)tmpl, tmpl->ie_count, tmpl->scope_count, tmpl->ie_len);
55 
56     for (i = 0; i < tmpl->ie_count; i++) {
57         fprintf(stderr,"\t%2u ", i);
58         fbInfoElementDebug(TRUE, tmpl->ie_ary[i]);
59     }
60 }
61 
fbTemplateAlloc(fbInfoModel_t * model)62 fbTemplate_t        *fbTemplateAlloc(
63     fbInfoModel_t       *model)
64 {
65     fbTemplate_t    *tmpl = NULL;
66 
67     /* create a new template */
68     tmpl = g_slice_new0(fbTemplate_t);
69 
70     /* fill it in */
71     tmpl->model = model;
72     tmpl->tmpl_len = 4;
73     tmpl->active = FALSE;
74 
75     /* allocate indices table */
76     tmpl->indices = g_hash_table_new((GHashFunc)fbInfoElementHash,
77                                      (GEqualFunc)fbInfoElementEqual);
78     return tmpl;
79 }
80 
81 
fbTemplateRetain(fbTemplate_t * tmpl)82 void                fbTemplateRetain(
83     fbTemplate_t        *tmpl)
84 {
85     /* Increment reference count */
86     ++(tmpl->ref_count);
87 }
88 
fbTemplateRelease(fbTemplate_t * tmpl)89 void                fbTemplateRelease(
90     fbTemplate_t        *tmpl)
91 {
92     /* Decrement reference count */
93     --(tmpl->ref_count);
94     /* Free if not referenced */
95     fbTemplateFreeUnused(tmpl);
96 }
97 
fbTemplateFreeUnused(fbTemplate_t * tmpl)98 void                fbTemplateFreeUnused(
99     fbTemplate_t        *tmpl)
100 {
101     if (tmpl->ref_count <= 0) {
102         fbTemplateFree(tmpl);
103     }
104 }
105 
fbTemplateFree(fbTemplate_t * tmpl)106 void                fbTemplateFree(
107     fbTemplate_t        *tmpl)
108 {
109     int                 i;
110 
111     if (tmpl->ctx_free) {
112         tmpl->ctx_free(tmpl->tmpl_ctx, tmpl->app_ctx);
113     }
114     /* destroy index table if present */
115     if (tmpl->indices) g_hash_table_destroy(tmpl->indices);
116 
117     /* destroy IE array */
118     for (i = 0; i < tmpl->ie_count; i++) {
119         g_slice_free(fbInfoElement_t, tmpl->ie_ary[i]);
120     }
121     g_free(tmpl->ie_ary);
122 
123     if (tmpl->metadata_rec) {
124         g_free(tmpl->metadata_rec->template_name.buf);
125         g_free(tmpl->metadata_rec->template_description.buf);
126         g_slice_free(fbTemplateOptRec_t, tmpl->metadata_rec);
127     }
128     /* destroy offset cache if present */
129     if (tmpl->off_cache) g_free(tmpl->off_cache);
130     /* destroy template */
131     g_slice_free(fbTemplate_t, tmpl);
132 
133 }
134 
fbTemplateExtendElements(fbTemplate_t * tmpl)135 static fbInfoElement_t *fbTemplateExtendElements(
136     fbTemplate_t        *tmpl)
137 {
138     if (tmpl->ie_count) {
139         tmpl->ie_ary =
140             g_renew(fbInfoElement_t*, tmpl->ie_ary, ++(tmpl->ie_count));
141     } else {
142         tmpl->ie_ary = g_new(fbInfoElement_t*, 1);
143         ++(tmpl->ie_count);
144     }
145 
146     tmpl->ie_ary[tmpl->ie_count - 1] = g_slice_new0(fbInfoElement_t);
147 
148     return tmpl->ie_ary[tmpl->ie_count - 1];
149 }
150 
fbTemplateExtendIndices(fbTemplate_t * tmpl,fbInfoElement_t * tmpl_ie)151 static void     fbTemplateExtendIndices(
152     fbTemplate_t        *tmpl,
153     fbInfoElement_t     *tmpl_ie)
154 {
155     void                *ign0, *ign1;
156 
157     /* search indices table for multiple IE index */
158     while (g_hash_table_lookup_extended(tmpl->indices, tmpl_ie, &ign0, &ign1)) {
159         ++(tmpl_ie->midx);
160     }
161 
162     /* increment template lengths */
163     tmpl->tmpl_len += tmpl_ie->ent ? 8 : 4;
164     if (tmpl_ie->len == FB_IE_VARLEN) {
165         tmpl->is_varlen = TRUE;
166         tmpl->ie_len += 1;
167         if (tmpl_ie->type == FB_BASIC_LIST) {
168             tmpl->ie_internal_len += sizeof(fbBasicList_t);
169         } else if (tmpl_ie->type == FB_SUB_TMPL_LIST) {
170             tmpl->ie_internal_len += sizeof(fbSubTemplateList_t);
171         } else if (tmpl_ie->type == FB_SUB_TMPL_MULTI_LIST) {
172             tmpl->ie_internal_len += sizeof(fbSubTemplateMultiList_t);
173         } else {
174             tmpl->ie_internal_len += sizeof(fbVarfield_t);
175         }
176     } else {
177         tmpl->ie_len += tmpl_ie->len;
178         tmpl->ie_internal_len += tmpl_ie->len;
179     }
180 
181     /* Add index of this information element to the indices table */
182     g_hash_table_insert(tmpl->indices, tmpl_ie,
183                         GUINT_TO_POINTER(tmpl->ie_count - 1));
184 }
185 
fbTemplateAppend(fbTemplate_t * tmpl,fbInfoElement_t * ex_ie,GError ** err)186 gboolean            fbTemplateAppend(
187     fbTemplate_t        *tmpl,
188     fbInfoElement_t     *ex_ie,
189     GError              **err)
190 {
191     fbInfoElement_t     *tmpl_ie;
192 
193     g_assert(ex_ie);
194 
195     /* grow information element array */
196     tmpl_ie = fbTemplateExtendElements(tmpl);
197 
198     /* copy information element out of the info model */
199     if (!fbInfoElementCopyToTemplate(tmpl->model, ex_ie, tmpl_ie)) {
200         g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_NOELEMENT,
201                     "No such information element %08x:%04x",
202                     ex_ie->ent, ex_ie->num);
203         return FALSE;
204     }
205 
206     /* Handle index and counter updates */
207     fbTemplateExtendIndices(tmpl, tmpl_ie);
208 
209     /* All done */
210     return TRUE;
211 }
212 
fbTemplateAppendSpec(fbTemplate_t * tmpl,fbInfoElementSpec_t * spec,uint32_t flags,GError ** err)213 gboolean            fbTemplateAppendSpec(
214     fbTemplate_t        *tmpl,
215     fbInfoElementSpec_t *spec,
216     uint32_t            flags,
217     GError              **err)
218 {
219     fbInfoElement_t     *tmpl_ie;
220 
221     /* Short-circuit on app flags mismatch */
222     if (spec->flags && !((spec->flags & flags) == spec->flags)) {
223         return TRUE;
224     }
225 
226     /* grow information element array */
227     tmpl_ie = fbTemplateExtendElements(tmpl);
228     /* copy information element out of the info model */
229 
230     if (!fbInfoElementCopyToTemplateByName(tmpl->model, spec->name,
231                                            spec->len_override, tmpl_ie)) {
232         g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_NOELEMENT,
233                     "No such information element %s", spec->name);
234         return FALSE;
235     }
236     if (spec->len_override == 0 && tmpl_ie->len != FB_IE_VARLEN) {
237         tmpl->default_length = TRUE;
238     }
239 
240     /* Handle index and counter updates */
241     fbTemplateExtendIndices(tmpl, tmpl_ie);
242 
243     /* All done */
244     return TRUE;
245 }
246 
fbTemplateAppendSpecArray(fbTemplate_t * tmpl,fbInfoElementSpec_t * spec,uint32_t flags,GError ** err)247 gboolean            fbTemplateAppendSpecArray(
248     fbTemplate_t        *tmpl,
249     fbInfoElementSpec_t *spec,
250     uint32_t            flags,
251     GError              **err)
252 {
253     for (; spec->name; spec++) {
254         if (!fbTemplateAppendSpec(tmpl, spec, flags, err)) {
255             return FALSE;
256         }
257     }
258 
259     return TRUE;
260 }
261 
fbTemplateSetOptionsScope(fbTemplate_t * tmpl,uint16_t scope_count)262 void                fbTemplateSetOptionsScope(
263     fbTemplate_t        *tmpl,
264     uint16_t            scope_count)
265 {
266     /* Cannot set options scope if we've already done so */
267     g_assert(!tmpl->scope_count);
268 
269     /* Cannot set scope count higher than IE count */
270     g_assert(tmpl->ie_count && tmpl->ie_count >= tmpl->scope_count);
271 
272     /* scope count of zero means make the last IE the end of scope */
273     tmpl->scope_count = scope_count ? scope_count : tmpl->ie_count;
274 
275     /* account for scope count in output */
276     tmpl->tmpl_len += 2;
277  }
278 
fbTemplateContainsElement(fbTemplate_t * tmpl,const fbInfoElement_t * ex_ie)279 gboolean           fbTemplateContainsElement(
280     fbTemplate_t            *tmpl,
281     const fbInfoElement_t   *ex_ie)
282 {
283     int i;
284 
285     if ( ex_ie == NULL || tmpl == NULL ) {
286         return FALSE;
287     }
288 
289     for (i = 0; i < tmpl->ie_count; i++) {
290         if (fbInfoElementEqual(ex_ie, tmpl->ie_ary[i])) return TRUE;
291     }
292 
293     return FALSE;
294 }
295 
fbTemplateContainsElementByName(fbTemplate_t * tmpl,fbInfoElementSpec_t * spec)296 gboolean           fbTemplateContainsElementByName(
297     fbTemplate_t        *tmpl,
298     fbInfoElementSpec_t *spec)
299 {
300     return fbTemplateContainsElement(
301         tmpl, fbInfoModelGetElementByName(tmpl->model, spec->name));
302 }
303 
fbTemplateContainsAllElementsByName(fbTemplate_t * tmpl,fbInfoElementSpec_t * spec)304 gboolean           fbTemplateContainsAllElementsByName(
305     fbTemplate_t        *tmpl,
306     fbInfoElementSpec_t *spec)
307 {
308     for (; spec->name; spec++) {
309         if (!fbTemplateContainsElementByName(tmpl, spec)) return FALSE;
310     }
311 
312     return TRUE;
313 }
314 
fbTemplateContainsAllFlaggedElementsByName(fbTemplate_t * tmpl,fbInfoElementSpec_t * spec,uint32_t flags)315 gboolean            fbTemplateContainsAllFlaggedElementsByName(
316     fbTemplate_t        *tmpl,
317     fbInfoElementSpec_t *spec,
318     uint32_t             flags)
319 {
320     for (; spec->name; spec++) {
321         if (spec->flags && !((spec->flags & flags) == spec->flags)) {
322             continue;
323         }
324         if (!fbTemplateContainsElementByName(tmpl, spec)) return FALSE;
325     }
326 
327     return TRUE;
328 }
329 
fbTemplateCountElements(fbTemplate_t * tmpl)330 uint32_t            fbTemplateCountElements(
331     fbTemplate_t        *tmpl)
332 {
333     return tmpl->ie_count;
334 }
335 
fbTemplateGetIndexedIE(fbTemplate_t * tmpl,uint32_t IEindex)336 fbInfoElement_t*    fbTemplateGetIndexedIE(
337     fbTemplate_t       *tmpl,
338     uint32_t            IEindex)
339 {
340     if (IEindex < tmpl->ie_count) {
341         return tmpl->ie_ary[IEindex];
342     } else {
343         return NULL;
344     }
345 }
346 
fbTemplateGetOptionsScope(fbTemplate_t * tmpl)347 uint32_t            fbTemplateGetOptionsScope(
348     fbTemplate_t        *tmpl)
349 {
350     return tmpl->scope_count;
351 }
352 
fbTemplateGetContext(fbTemplate_t * tmpl)353 void               *fbTemplateGetContext(
354     fbTemplate_t        *tmpl)
355 {
356     return tmpl->tmpl_ctx;
357 }
358 
fbTemplateGetIELenOfMemBuffer(fbTemplate_t * tmpl)359 uint16_t            fbTemplateGetIELenOfMemBuffer(
360     fbTemplate_t         *tmpl)
361 {
362     return tmpl->ie_internal_len;
363 }
364 
fbTemplateGetInfoModel(fbTemplate_t * tmpl)365 fbInfoModel_t      *fbTemplateGetInfoModel(
366     fbTemplate_t        *tmpl)
367 {
368     return tmpl->model;
369 }
370 
fbTemplateAllocTemplateMetadataTmpl(fbInfoModel_t * model,gboolean internal,GError ** err)371 fbTemplate_t        *fbTemplateAllocTemplateMetadataTmpl(
372     fbInfoModel_t      *model,
373     gboolean            internal,
374     GError            **err)
375 {
376     fbTemplate_t *tmpl;
377     uint32_t flags;
378 
379     /* do not include padding when external */
380     flags = internal ? ~0 : 0;
381 
382     tmpl = fbTemplateAlloc(model);
383     if (!fbTemplateAppendSpecArray(tmpl, template_metadata_spec, flags, err)) {
384         fbTemplateFreeUnused(tmpl);
385         return NULL;
386     }
387     fbTemplateSetOptionsScope(tmpl, 1);
388     return tmpl;
389 }
390 
fbTemplateAddMetadataRecord(fbTemplate_t * tmpl,uint16_t tid,const char * name,const char * description)391 void                fbTemplateAddMetadataRecord(
392     fbTemplate_t        *tmpl,
393     uint16_t             tid,
394     const char          *name,
395     const char          *description)
396 {
397     fbTemplateOptRec_t *metadata_rec;
398 
399     metadata_rec = g_slice_new0(fbTemplateOptRec_t);
400     metadata_rec->template_id = tid;
401     metadata_rec->template_name.buf = (uint8_t *) g_strdup(name);
402     metadata_rec->template_name.len = strlen(name);
403 
404     if (description) {
405         metadata_rec->template_description.buf =
406             (uint8_t *) g_strdup(description);
407         metadata_rec->template_description.len = strlen(description);
408     }
409 
410     if (tmpl->metadata_rec) {
411         g_free(tmpl->metadata_rec->template_name.buf);
412         g_free(tmpl->metadata_rec->template_description.buf);
413         g_slice_free(fbTemplateOptRec_t, tmpl->metadata_rec);
414     }
415 
416     tmpl->metadata_rec = metadata_rec;
417 }
418 
419