1 /*
2  ** fbsession.c
3  ** IPFIX Transport Session state container
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 /* whether to debug writing of InfoElement and Template metadata */
45 #ifndef FB_DEBUG_MD
46 #define FB_DEBUG_MD 0
47 #endif
48 
49 /** Size of the tmpl_pair_array */
50 #define TMPL_PAIR_ARRAY_SIZE    (sizeof(uint16_t) * (1 << 16))
51 
52 #if HAVE_SPREAD
53 /* Lock the mutex on session 's' when spread is active */
54 #define FB_SPREAD_MUTEX_LOCK(s)     pthread_mutex_lock(&(s)->ext_ttab_wlock)
55 
56 /* Unlock the mutex on session 's' when spread is active */
57 #define FB_SPREAD_MUTEX_UNLOCK(s)   pthread_mutex_unlock(&(s)->ext_ttab_wlock)
58 #else
59 #define FB_SPREAD_MUTEX_LOCK(s)
60 #define FB_SPREAD_MUTEX_UNLOCK(s)
61 #endif  /* HAVE_SPREAD */
62 
63 /* FIXME: Consider changing fbSession so the ext_FOO/int_FOO pairs of
64  * members become a FOO[2] array and the `internal` gboolean used by
65  * several function is used as the index into those arrays. */
66 
67 struct fbSession_st {
68     /** Information model. */
69     fbInfoModel_t               *model;
70     /**
71      * Internal template table. Maps template ID to internal template.
72      */
73     GHashTable                  *int_ttab;
74     /**
75      * External template table for current observation domain.
76      * Maps template ID to external template.
77      */
78     GHashTable                  *ext_ttab;
79     /**
80      * Array of size 2^16 where index is external TID and value is
81      * internal TID.  The number if valid entries in the array is
82      * maintained by 'num_tmpl_pairs'.
83      */
84     uint16_t                    *tmpl_pair_array;
85     /**
86      * Callback function to allow an application to assign template
87      * pairs for transcoding purposes, and to add context to a
88      * particular template.  Uses 'tmpl_app_ctx'.
89      */
90     fbNewTemplateCallback_fn    new_template_callback;
91     /**
92      * Context the caller provides for 'new_template_callback'.
93      */
94     void                        *tmpl_app_ctx;
95     /**
96      * A pointer to the largest template seen in this session.  The
97      * size of this template 'largestInternalTemplateLength'.
98      */
99     fbTemplate_t               *largestInternalTemplate;
100     /**
101      * Domain external template table.
102      * Maps domain to external template table.
103      */
104     GHashTable                  *dom_ttab;
105     /**
106      * Domain last/next sequence number table.
107      * Maps domain to sequence number.
108      */
109     GHashTable                  *dom_seqtab;
110     /**
111      * Current observation domain ID.
112      */
113     uint32_t                    domain;
114     /**
115      * Last/next sequence number in current observation domain.
116      */
117     uint32_t                    sequence;
118     /**
119      * Pointer to collector that was created with session
120      */
121     fbCollector_t               *collector;
122     /**
123      * Buffer instance to write template dynamics to.
124      */
125     fBuf_t                      *tdyn_buf;
126     /**
127      * Error description for fbSessionExportTemplates()
128      */
129     GError                      *tdyn_err;
130     /**
131      * The number of valid pairs in 'tmpl_pair_array'.  Used to free
132      * the array when it is empty.
133      */
134     uint16_t                    num_tmpl_pairs;
135     /**
136      * The TID to use for exporting enterprise-specific IEs when
137      * export_info_element_metadata is true.
138      */
139     uint16_t                    info_element_metadata_tid;
140     /**
141      * The TID to use for exporting template metadata when
142      * export_template_metadata is true.
143      */
144     uint16_t                    template_metadata_tid;
145     /**
146      * The length of the template in 'largestInternalTemplate'.
147      */
148     uint16_t                    largestInternalTemplateLength;
149     /**
150      * Where to begin looking for an unused external template ID when
151      * the ID is FB_TID_AUTO.  These begin at FB_TID_MIN_DATA and
152      * increment.
153      */
154     uint16_t                    ext_next_tid;
155     /**
156      * Where to begin looking for an unused internal template ID when
157      * the ID is FB_TID_AUTO.  These begin at UINT16_MAX and
158      * decrement.
159      */
160     uint16_t                    int_next_tid;
161     /**
162      * If TRUE, options records will be exported for
163      * enterprise-specific IEs.  The TID used is
164      * 'info_element_metadata_tid'.
165      */
166     gboolean                    export_info_element_metadata;
167     /**
168      * If TRUE, options records will be exported for templates that
169      * have been given a name.  The TID used is
170      * 'template_metadata_tid'.
171      */
172     gboolean                    export_template_metadata;
173     /**
174      * Flag to set when an internal template is added or removed. The flag is
175      * unset when SetInternalTemplate is called. This ensures the internal
176      * template set to the most up-to-date template
177      */
178     gboolean                     intTmplTableChanged;
179     /**
180      * Flag to set when an external template is added or removed. The flag is
181      * unset when SetExportTemplate is called. This ensures the external
182      * template set to the most up-to-date template
183      */
184     gboolean                     extTmplTableChanged;
185 
186 
187 #if HAVE_SPREAD
188     /**
189      * Mutex to guard the ext_ttab when using Spread.  Spread listens for new
190      * group memberships.  On a new membership, the Spread transport will send
191      * all external templates to the new member privately.  During this
192      * process, the external template table cannot be modified, hence the
193      * write lock on ext_ttab.
194      */
195     pthread_mutex_t             ext_ttab_wlock;
196     /**
197      * Group External Template Table.
198      * Maps group to the external template table
199      */
200     GHashTable                  *grp_ttab;
201     /**
202      * Group Sequence Number Table
203      * Maps group to sequence number (only looks at first group in list)
204      */
205     GHashTable                  *grp_seqtab;
206     /**
207      * Need to keep track of all groups session knows about
208      */
209     sp_groupname_t              *all_groups;
210     /**
211      * Number of groups session knows about.
212      */
213     unsigned int                num_groups;
214     /**
215      * Current Group
216      */
217     unsigned int                group;
218 #endif  /* HAVE_SPREAD */
219 };
220 
221 static void fbSessionSetLargestInternalTemplateLen(
222     fbSession_t    *session);
223 
224 static uint16_t fbSessionAddTemplateHelper(
225     fbSession_t    *session,
226     gboolean        internal,
227     uint16_t        tid,
228     fbTemplate_t   *tmpl,
229     const char     *name,
230     const char     *description,
231     GError         **err);
232 
233 #if HAVE_SPREAD
234 static uint16_t fbSessionSpreadAddTemplatesHelper(
235     fbSession_t    *session,
236     char           **groups,
237     gboolean        internal,
238     uint16_t        tid,
239     fbTemplate_t   *tmpl,
240     char           *name,
241     char           *description,
242     GError         **err);
243 #endif  /* HAVE_SPREAD */
244 
fbSessionAlloc(fbInfoModel_t * model)245 fbSession_t     *fbSessionAlloc(
246     fbInfoModel_t   *model)
247 {
248     fbSession_t     *session = NULL;
249 
250     /* Create a new session */
251     session = g_slice_new0(fbSession_t);
252 
253     /* Store reference to information model */
254     session->model = model;
255 
256     /* Allocate internal template table */
257     session->int_ttab = g_hash_table_new(g_direct_hash, g_direct_equal);
258 
259 #if HAVE_SPREAD
260     /* this lock is needed only if Spread is enabled */
261     pthread_mutex_init( &session->ext_ttab_wlock, 0 );
262 #endif
263 
264     /* Reset session externals (will allocate domain template tables, etc.) */
265     fbSessionResetExternal(session);
266 
267     session->tmpl_pair_array = NULL;
268     session->num_tmpl_pairs = 0;
269     session->new_template_callback = NULL;
270 
271     session->int_next_tid = UINT16_MAX;
272     session->ext_next_tid = FB_TID_MIN_DATA;
273 
274     /* All done */
275     return session;
276 }
277 
fbSessionEnableTypeMetadata(fbSession_t * session,gboolean enabled,GError ** err)278 gboolean fbSessionEnableTypeMetadata(
279     fbSession_t                *session,
280     gboolean                    enabled,
281     GError                     **err)
282 {
283     return (fbSessionSetMetadataExportElements(
284                 session, enabled, FB_TID_AUTO, err) != 0);
285 }
286 
fbSessionSetMetadataExportElements(fbSession_t * session,gboolean enabled,uint16_t tid,GError ** err)287 uint16_t fbSessionSetMetadataExportElements(
288     fbSession_t                *session,
289     gboolean                    enabled,
290     uint16_t                    tid,
291     GError                     **err)
292 {
293     fbTemplate_t *ie_type_template = NULL;
294 
295     session->export_info_element_metadata = enabled;
296 
297     /* external */
298     ie_type_template = fbInfoElementAllocTypeTemplate2(session->model,
299                                                        FALSE, err);
300     if (! ie_type_template) {
301         return 0;
302     }
303     session->info_element_metadata_tid = fbSessionAddTemplateHelper(
304         session, FALSE, tid, ie_type_template, NULL, NULL, err);
305     if (! session->info_element_metadata_tid) {
306         fbTemplateFreeUnused(ie_type_template);
307         return 0;
308     }
309 
310     /* internal */
311     ie_type_template = fbInfoElementAllocTypeTemplate2(session->model,
312                                                        TRUE, err);
313     if (! ie_type_template) {
314         return 0;
315     }
316     session->info_element_metadata_tid = fbSessionAddTemplateHelper(
317         session, TRUE, session->info_element_metadata_tid,
318         ie_type_template, NULL, NULL, err);
319     if (! session->info_element_metadata_tid) {
320         fbTemplateFreeUnused(ie_type_template);
321         return 0;
322     }
323 
324     return session->info_element_metadata_tid;
325 }
326 
327 
328 /*
329  *  Writes the info element type metadata for all non-standard
330  *  elements in the info model to the template dynamics buffer of
331  *  'session'.
332  *
333  *  Does NOT restore the internal and external templates of the fBuf.
334  */
fbSessionWriteTypeMetadata(fbSession_t * session,GError ** err)335 static gboolean fbSessionWriteTypeMetadata(
336     fbSession_t                *session,
337     GError                    **err)
338 {
339     fbInfoModelIter_t iter;
340     const fbInfoElement_t *ie = NULL;
341     GError *child_err = NULL;
342 
343     if (!session->export_info_element_metadata) {
344 #if FB_DEBUG_MD
345         fprintf(stderr, "Called fbSessionWriteTypeMetadata on a session"
346                 " that is not configured for element metadata\n");
347 #endif
348         return TRUE;
349     }
350 
351 #if FB_DEBUG_MD
352     fprintf(stderr, "Writing info element metadata tmpl %#x to fbuf %p\n",
353             session->info_element_metadata_tid, (void *)session->tdyn_buf);
354 #endif
355 
356     if (!fBufSetInternalTemplate(
357             session->tdyn_buf, session->info_element_metadata_tid, &child_err))
358     {
359 #if FB_DEBUG_MD
360         fprintf(stderr, "fBufSetInternalTemplate(%#x) failed: %s\n",
361                 session->info_element_metadata_tid, child_err->message);
362 #endif
363         g_propagate_error(err, child_err);
364         return FALSE;
365     }
366 
367     if (!fBufSetExportTemplate(
368             session->tdyn_buf, session->info_element_metadata_tid, &child_err))
369     {
370         if (g_error_matches(child_err, FB_ERROR_DOMAIN, FB_ERROR_TMPL)) {
371 #if FB_DEBUG_MD
372             fprintf(stderr,"Ignoring failed fBufSetExternalTemplate(%#x): %s\n",
373                     session->info_element_metadata_tid, child_err->message);
374 #endif
375             g_clear_error(&child_err);
376             return TRUE;
377         }
378 #if FB_DEBUG_MD
379         fprintf(stderr, "fBufSetExternalTemplate(%#x) failed: %s\n",
380                 session->info_element_metadata_tid, child_err->message);
381 #endif
382         g_propagate_error(err, child_err);
383         return FALSE;
384     }
385 
386     fbInfoModelIterInit(&iter, session->model);
387 
388     while ( (ie = fbInfoModelIterNext(&iter)) ) {
389         /* No need to send metadata for IEs in the standard info model */
390         if (ie->ent == 0 || ie->ent == FB_IE_PEN_REVERSE) {
391             continue;
392         }
393 
394         if (!fbInfoElementWriteOptionsRecord(
395             session->tdyn_buf, ie, session->info_element_metadata_tid,
396             session->info_element_metadata_tid, &child_err))
397         {
398 #if FB_DEBUG_MD
399             fprintf(stderr,"fbInfoElementWriteOptionsRecord(%s) failed: %s\n",
400                     ie->ref.name, child_err->message);
401 #endif
402             g_propagate_error(err, child_err);
403             return FALSE;
404         }
405     }
406 
407 #if FB_DEBUG_MD
408     fprintf(stderr, "Finished writing info element type metadata\n");
409 #endif
410     return TRUE;
411 }
412 
fbSessionEnableTemplateMetadata(fbSession_t * session,gboolean enabled,GError ** err)413 gboolean fbSessionEnableTemplateMetadata(
414     fbSession_t                *session,
415     gboolean                    enabled,
416     GError                    **err)
417 {
418     return (fbSessionSetMetadataExportTemplates(
419                 session, enabled, FB_TID_AUTO, err) != 0);
420 }
421 
fbSessionSetMetadataExportTemplates(fbSession_t * session,gboolean enabled,uint16_t tid,GError ** err)422 uint16_t fbSessionSetMetadataExportTemplates(
423     fbSession_t                *session,
424     gboolean                    enabled,
425     uint16_t                    tid,
426     GError                    **err)
427 {
428     fbTemplate_t *metadata_template = NULL;
429 
430     session->export_template_metadata = enabled;
431 
432     /* external */
433     metadata_template = fbTemplateAllocTemplateMetadataTmpl(session->model,
434                                                             FALSE, err);
435     if (!metadata_template) {
436         return 0;
437     }
438     session->template_metadata_tid = fbSessionAddTemplateHelper(
439         session, FALSE, tid, metadata_template, NULL, NULL, err);
440     if (!session->template_metadata_tid) {
441         fbTemplateFreeUnused(metadata_template);
442         return 0;
443     }
444 
445     /* internal */
446     metadata_template = fbTemplateAllocTemplateMetadataTmpl(session->model,
447                                                             TRUE, err);
448     if (!metadata_template) {
449         return 0;
450     }
451     session->template_metadata_tid = fbSessionAddTemplateHelper(
452         session, TRUE, session->template_metadata_tid,
453         metadata_template, NULL, NULL, err);
454     if (!session->template_metadata_tid) {
455         /* should it be removed from the external session? */
456         fbTemplateFreeUnused(metadata_template);
457         return 0;
458     }
459 
460     return session->template_metadata_tid;
461 }
462 
463 #if HAVE_SPREAD
fbSessionSpreadEnableTemplateMetadata(fbSession_t * session,char ** groups,gboolean enabled,GError ** err)464 gboolean fbSessionSpreadEnableTemplateMetadata(
465     fbSession_t                *session,
466     char                       **groups,
467     gboolean                   enabled,
468     GError                     **err)
469 {
470     return (fbSessionSpreadSetMetadataExportTemplates(
471                 session, groups, enabled, FB_TID_AUTO, err) != 0);
472 }
473 
fbSessionSpreadSetMetadataExportTemplates(fbSession_t * session,char ** groups,gboolean enabled,uint16_t tid,GError ** err)474 uint16_t fbSessionSpreadSetMetadataExportTemplates(
475     fbSession_t                *session,
476     char                       **groups,
477     gboolean                   enabled,
478     uint16_t                    tid,
479     GError                     **err)
480 {
481     fbTemplate_t *metadata_template = NULL;
482 
483     session->export_template_metadata = enabled;
484 
485     /* external */
486     metadata_template = fbTemplateAllocTemplateMetadataTmpl(session->model,
487                                                             FALSE, err);
488     if (!metadata_template) {
489         return 0;
490     }
491     session->template_metadata_tid = fbSessionSpreadAddTemplatesHelper(
492         session, groups, FALSE, tid, metadata_template, NULL, NULL, err);
493     if (!session->template_metadata_tid) {
494         fbTemplateFreeUnused(metadata_template);
495         return 0;
496     }
497 
498     /* internal */
499     metadata_template = fbTemplateAllocTemplateMetadataTmpl(session->model,
500                                                             TRUE, err);
501     if (!metadata_template) {
502         return 0;
503     }
504     session->template_metadata_tid = fbSessionAddTemplateHelper(
505         session, TRUE, session->template_metadata_tid,
506         metadata_template, NULL, NULL, err);
507     if (!session->template_metadata_tid) {
508         /* should it be removed from the external session? */
509         fbTemplateFreeUnused(metadata_template);
510         return 0;
511     }
512 
513     return session->template_metadata_tid;
514 }
515 
fbSessionSpreadEnableTypeMetadata(fbSession_t * session,char ** groups,gboolean enabled,GError ** err)516 gboolean fbSessionSpreadEnableTypeMetadata(
517     fbSession_t                *session,
518     char                       **groups,
519     gboolean                   enabled,
520     GError                     **err)
521 {
522     return (fbSessionSpreadSetMetadataExportElements(
523                 session, groups, enabled, FB_TID_AUTO, err) != 0);
524 }
525 
fbSessionSpreadSetMetadataExportElements(fbSession_t * session,char ** groups,gboolean enabled,uint16_t tid,GError ** err)526 uint16_t fbSessionSpreadSetMetadataExportElements(
527     fbSession_t                *session,
528     char                       **groups,
529     gboolean                   enabled,
530     uint16_t                   tid,
531     GError                     **err)
532 {
533     fbTemplate_t *ie_type_template = NULL;
534 
535     session->export_info_element_metadata = enabled;
536 
537     /* external */
538     ie_type_template = fbInfoElementAllocTypeTemplate2(session->model,
539                                                        FALSE, err);
540     if (!ie_type_template) {
541         return 0;
542     }
543     session->info_element_metadata_tid = fbSessionSpreadAddTemplatesHelper(
544         session, groups, FALSE, tid, ie_type_template, NULL, NULL, err);
545     if (!session->info_element_metadata_tid) {
546         fbTemplateFreeUnused(ie_type_template);
547         return 0;
548     }
549 
550     /* internal */
551     ie_type_template = fbInfoElementAllocTypeTemplate2(session->model,
552                                                        TRUE, err);
553     session->info_element_metadata_tid = fbSessionAddTemplateHelper(
554         session, TRUE, session->info_element_metadata_tid,
555         ie_type_template, NULL, NULL, err);
556     if (!session->info_element_metadata_tid) {
557         fbTemplateFreeUnused(ie_type_template);
558         return 0;
559     }
560 
561     return session->info_element_metadata_tid;
562 }
563 
564 #endif  /* HAVE_SPREAD */
565 
566 
567 /* Writes the metadata for 'tmpl' to 'session'. */
fbSessionWriteTemplateMetadata(fbSession_t * session,fbTemplate_t * tmpl,GError ** err)568 static gboolean fbSessionWriteTemplateMetadata(
569     fbSession_t    *session,
570     fbTemplate_t   *tmpl,
571     GError         **err)
572 {
573     uint16_t int_tid, ext_tid;
574     GError *child_err = NULL;
575     gboolean ret = TRUE;
576 
577     if (!session->export_template_metadata || !tmpl->metadata_rec) {
578         return TRUE;
579     }
580 #if FB_DEBUG_MD
581     fprintf(stderr, "writing metadata for template %p to fBuf %p\n",
582             (void *)tmpl, (void *)session->tdyn_buf);
583 #endif
584 
585     int_tid = fBufGetInternalTemplate(session->tdyn_buf);
586     ext_tid = fBufGetExportTemplate(session->tdyn_buf);
587 
588     if (!fBufSetInternalTemplate(
589             session->tdyn_buf, session->template_metadata_tid, err))
590     {
591 #if FB_DEBUG_MD
592         fprintf(stderr, "fBufSetInternalTemplate(%#x) failed: %s\n",
593                 session->template_metadata_tid, (err ? (*err)->message : ""));
594 #endif
595         ret = FALSE;
596         goto RESET_INT;
597     }
598     if (!fBufSetExportTemplate(
599             session->tdyn_buf, session->template_metadata_tid, err))
600     {
601 #if FB_DEBUG_MD
602         fprintf(stderr, "fBufSetExportTemplate(%#x) failed: %s\n",
603                 session->template_metadata_tid, (err ? (*err)->message : ""));
604 #endif
605         ret = FALSE;
606         goto RESET_EXT;
607     }
608     if (!fBufAppend(session->tdyn_buf, (uint8_t *)tmpl->metadata_rec,
609                     sizeof(fbTemplateOptRec_t), err))
610     {
611 #if FB_DEBUG_MD
612         fprintf(stderr, "fBufAppend(%p) failed: %s\n",
613                 (void *)tmpl->metadata_rec, (err ? (*err)->message : ""));
614 #endif
615         ret = FALSE;
616         goto RESET_EXT;
617     }
618     /* if (!fBufEmit(session->tdyn_buf, err)) goto END; */
619 
620   RESET_EXT:
621     if (ext_tid
622         && !fBufSetExportTemplate(session->tdyn_buf, ext_tid, &child_err))
623     {
624 #if FB_DEBUG_MD
625         fprintf(stderr, "restore fBufSetExportTemplate(%#x) failed: %s\n",
626                 ext_tid, child_err->message);
627 #endif
628         if (!ret || g_error_matches(child_err, FB_ERROR_DOMAIN, FB_ERROR_TMPL)){
629             /* ignore this error if either an error is already set or
630              * this error is an unknown template error */
631             g_clear_error(&child_err);
632         } else {
633             g_propagate_error(err, child_err);
634             child_err = NULL;
635             ret = FALSE;
636         }
637     }
638   RESET_INT:
639     if (int_tid
640         && !fBufSetInternalTemplate(session->tdyn_buf, int_tid, &child_err))
641     {
642 #if FB_DEBUG_MD
643         fprintf(stderr, "restore fBufSetInternalTemplate(%#x) failed: %s\n",
644                 int_tid, child_err->message);
645 #endif
646         if (!ret || g_error_matches(child_err, FB_ERROR_DOMAIN, FB_ERROR_TMPL)){
647             g_clear_error(&child_err);
648         } else {
649             g_propagate_error(err, child_err);
650             child_err = NULL;
651             ret = FALSE;
652         }
653     }
654 
655     return ret;
656 }
657 
658 
fbSessionAddTemplateWithMetadata(fbSession_t * session,gboolean internal,uint16_t tid,fbTemplate_t * tmpl,const char * name,const char * description,GError ** err)659 uint16_t fbSessionAddTemplateWithMetadata(
660     fbSession_t         *session,
661     gboolean             internal,
662     uint16_t             tid,
663     fbTemplate_t         *tmpl,
664     const char           *name,
665     const char           *description,
666     GError               **err)
667 {
668     if (!name) {
669         g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_SETUP,
670                     "Template name must be specified");
671         return 0;
672     }
673     return fbSessionAddTemplateHelper(session, internal, tid, tmpl,
674                                       name, description, err);
675 }
676 
fbSessionNewTemplateCallback(fbSession_t * session)677 fbNewTemplateCallback_fn fbSessionNewTemplateCallback(
678     fbSession_t *session)
679 {
680     return session->new_template_callback;
681 }
682 
fbSessionAddNewTemplateCallback(fbSession_t * session,fbNewTemplateCallback_fn callback,void * app_ctx)683 void fbSessionAddNewTemplateCallback(
684     fbSession_t                 *session,
685     fbNewTemplateCallback_fn    callback,
686     void                        *app_ctx)
687 {
688     session->new_template_callback = callback;
689     session->tmpl_app_ctx = app_ctx;
690 }
691 
fbSessionNewTemplateCallbackAppCtx(fbSession_t * session)692 void *fbSessionNewTemplateCallbackAppCtx(
693     fbSession_t *session)
694 {
695     return session->tmpl_app_ctx;
696 }
697 
fbSessionAddTemplatePair(fbSession_t * session,uint16_t ext_tid,uint16_t int_tid)698 void fbSessionAddTemplatePair(
699     fbSession_t    *session,
700     uint16_t        ext_tid,
701     uint16_t        int_tid)
702 {
703     gboolean madeTable = FALSE;
704 
705     if (!session->tmpl_pair_array) {
706         session->tmpl_pair_array =
707             (uint16_t*)g_slice_alloc0(TMPL_PAIR_ARRAY_SIZE);
708         madeTable = TRUE;
709     }
710 
711     if ((ext_tid == int_tid) || (int_tid == 0)) {
712         session->tmpl_pair_array[ext_tid] = int_tid;
713         session->num_tmpl_pairs++;
714         return;
715     }
716 
717     /* external and internal tids are different */
718     /* only add the template pair if the internal template exists */
719     if (fbSessionGetTemplate(session, TRUE, int_tid, NULL)) {
720         session->tmpl_pair_array[ext_tid] = int_tid;
721         session->num_tmpl_pairs++;
722     } else {
723         if (madeTable) {
724             g_slice_free1(TMPL_PAIR_ARRAY_SIZE, session->tmpl_pair_array);
725             session->tmpl_pair_array = NULL;
726         }
727     }
728 }
729 
fbSessionRemoveTemplatePair(fbSession_t * session,uint16_t ext_tid)730 void fbSessionRemoveTemplatePair(
731     fbSession_t    *session,
732     uint16_t        ext_tid)
733 {
734     if (!session->tmpl_pair_array) {
735         return;
736     }
737 
738     if (session->tmpl_pair_array[ext_tid]) {
739         session->num_tmpl_pairs--;
740         if (!session->num_tmpl_pairs) {
741             /* this was the last one, free the array */
742             g_slice_free1(TMPL_PAIR_ARRAY_SIZE, session->tmpl_pair_array);
743             session->tmpl_pair_array = NULL;
744             return;
745         }
746         session->tmpl_pair_array[ext_tid] = 0;
747     }
748 }
749 
fbSessionLookupTemplatePair(fbSession_t * session,uint16_t ext_tid)750 uint16_t    fbSessionLookupTemplatePair(
751     fbSession_t    *session,
752     uint16_t        ext_tid)
753 {
754     /* if there are no current pairs, just return ext_tid because that means
755      * we should decode the entire external template
756      */
757     if (!session->tmpl_pair_array) {
758         return ext_tid;
759     }
760 
761     return session->tmpl_pair_array[ext_tid];
762 }
763 
fbSessionFreeOneTemplate(void * vtid,fbTemplate_t * tmpl,fbSession_t * session)764 static void     fbSessionFreeOneTemplate(
765     void            *vtid __attribute__((unused)),
766     fbTemplate_t    *tmpl,
767     fbSession_t     *session)
768 {
769     fBufRemoveTemplateTcplan(session->tdyn_buf, tmpl);
770     fbTemplateRelease(tmpl);
771 }
772 
fbSessionResetOneDomain(void * vdomain,GHashTable * ttab,fbSession_t * session)773 static void     fbSessionResetOneDomain(
774     void            *vdomain __attribute__((unused)),
775     GHashTable      *ttab,
776     fbSession_t     *session)
777 {
778     g_hash_table_foreach(ttab,
779                          (GHFunc)fbSessionFreeOneTemplate, session);
780 }
781 
fbSessionResetExternal(fbSession_t * session)782 void            fbSessionResetExternal(
783     fbSession_t     *session)
784 {
785     /* Clear out the old domain template table if we have one */
786     if (session->dom_ttab) {
787         /* Release all the external templates (will free unless shared) */
788         g_hash_table_foreach(session->dom_ttab,
789                             (GHFunc)fbSessionResetOneDomain, session);
790         /* Nuke the domain template table */
791         g_hash_table_destroy(session->dom_ttab);
792     }
793 
794     /* Allocate domain template table */
795     session->dom_ttab =
796         g_hash_table_new_full(g_direct_hash, g_direct_equal,
797                               NULL, (GDestroyNotify)g_hash_table_destroy);
798 
799     /* Null out stale external template table */
800     FB_SPREAD_MUTEX_LOCK(session);
801     session->ext_ttab = NULL;
802     FB_SPREAD_MUTEX_UNLOCK(session);
803 
804     /* Clear out the old sequence number table if we have one */
805     if (session->dom_seqtab) {
806         g_hash_table_destroy(session->dom_seqtab);
807     }
808 
809     /* Allocate domain sequence number table */
810     session->dom_seqtab =
811         g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
812                               NULL);
813 
814     /* Zero sequence number and domain */
815     session->sequence = 0;
816     session->domain = 0;
817 
818 #if HAVE_SPREAD
819     if (session->grp_ttab) {
820         g_hash_table_destroy(session->grp_ttab);
821     }
822     /*Allocate group template table */
823     session->grp_ttab =
824         g_hash_table_new_full(g_direct_hash, g_direct_equal,
825                               NULL, (GDestroyNotify)g_hash_table_destroy);
826     if (session->grp_seqtab) {
827         g_hash_table_destroy(session->grp_seqtab);
828     }
829     session->grp_seqtab = g_hash_table_new_full(g_direct_hash, g_direct_equal,
830                                                 NULL, NULL);
831 
832     /** Group 0 just means if we never sent a template on a group -
833      * we will multicast to every group we know about */
834 
835     session->group = 0;
836 #endif  /* HAVE_SPREAD */
837 
838     /* Set domain to 0 (initializes external template table) */
839     fbSessionSetDomain(session, 0);
840 }
841 
fbSessionFree(fbSession_t * session)842 void            fbSessionFree(
843     fbSession_t     *session)
844 {
845     if (NULL == session) {
846         return;
847     }
848     fbSessionResetExternal(session);
849     g_hash_table_foreach(session->int_ttab,
850                          (GHFunc)fbSessionFreeOneTemplate, session);
851     g_hash_table_destroy(session->int_ttab);
852     g_hash_table_destroy(session->dom_ttab);
853     if (session->dom_seqtab) {
854         g_hash_table_destroy(session->dom_seqtab);
855     }
856     g_slice_free1(TMPL_PAIR_ARRAY_SIZE, session->tmpl_pair_array);
857     session->tmpl_pair_array = NULL;
858 #if HAVE_SPREAD
859     if (session->grp_ttab) {
860         g_hash_table_destroy(session->grp_ttab);
861     }
862     if (session->grp_seqtab) {
863         g_hash_table_destroy(session->grp_seqtab);
864     }
865     pthread_mutex_destroy(&session->ext_ttab_wlock);
866 #endif  /* HAVE_SPREAD */
867     g_slice_free(fbSession_t, session);
868 }
869 
fbSessionSetDomain(fbSession_t * session,uint32_t domain)870 void            fbSessionSetDomain(
871     fbSession_t     *session,
872     uint32_t        domain)
873 {
874     /* Short-circuit identical domain if not initializing */
875     if (session->ext_ttab && (domain == session->domain)) return;
876 
877     /* Update external template table; create if necessary. */
878     FB_SPREAD_MUTEX_LOCK(session);
879     session->ext_ttab = g_hash_table_lookup( session->dom_ttab,
880                                              GUINT_TO_POINTER(domain) );
881     if (!session->ext_ttab)
882     {
883         session->ext_ttab = g_hash_table_new(g_direct_hash, g_direct_equal);
884         g_hash_table_insert(session->dom_ttab, GUINT_TO_POINTER(domain),
885                             session->ext_ttab);
886     }
887     FB_SPREAD_MUTEX_UNLOCK(session);
888 
889     /* Stash current sequence number */
890     g_hash_table_insert(session->dom_seqtab,
891                         GUINT_TO_POINTER(session->domain),
892                         GUINT_TO_POINTER(session->sequence));
893 
894     /* Get new sequence number */
895     session->sequence = GPOINTER_TO_UINT(
896         g_hash_table_lookup(session->dom_seqtab,GUINT_TO_POINTER(domain)));
897 
898     /* Stash new domain */
899     session->domain = domain;
900 }
901 
902 /**
903  * Find an unused template ID in the template table of `session`.
904  */
fbSessionFindUnusedTemplateId(fbSession_t * session,gboolean internal)905 static uint16_t fbSessionFindUnusedTemplateId(
906     fbSession_t     *session,
907     gboolean        internal)
908 {
909     /* Select a template table to add the template to */
910     GHashTable *ttab = internal ? session->int_ttab : session->ext_ttab;
911     uint16_t tid = 0;
912 
913     if (internal) {
914         if (g_hash_table_size(ttab) == (UINT16_MAX - FB_TID_MIN_DATA)) {
915             return 0;
916         }
917         tid = session->int_next_tid;
918         while (g_hash_table_lookup(ttab, GUINT_TO_POINTER((unsigned int)tid))){
919             tid = ((tid > FB_TID_MIN_DATA) ? (tid - 1) : UINT16_MAX);
920         }
921         session->int_next_tid =
922             ((tid > FB_TID_MIN_DATA) ? (tid - 1) : UINT16_MAX);
923     } else {
924         FB_SPREAD_MUTEX_LOCK(session);
925         if (g_hash_table_size(ttab) == (UINT16_MAX - FB_TID_MIN_DATA)) {
926             FB_SPREAD_MUTEX_UNLOCK(session);
927             return 0;
928         }
929         tid = session->ext_next_tid;
930         while (g_hash_table_lookup(ttab, GUINT_TO_POINTER((unsigned int)tid))){
931             tid = ((tid < UINT16_MAX) ? (tid + 1) : FB_TID_MIN_DATA);
932         }
933         session->ext_next_tid =
934             ((tid < UINT16_MAX) ? (tid + 1) : FB_TID_MIN_DATA);
935         FB_SPREAD_MUTEX_UNLOCK(session);
936     }
937     return tid;
938 }
939 
940 #if HAVE_SPREAD
fbSessionSetGroupParams(fbSession_t * session,sp_groupname_t * groups,unsigned int num_groups)941 void fbSessionSetGroupParams(
942     fbSession_t     *session,
943     sp_groupname_t  *groups,
944     unsigned int     num_groups)
945 {
946     session->all_groups = groups;
947     session->num_groups = num_groups;
948 }
949 
fbSessionGetGroupOffset(fbSession_t * session,char * group)950 unsigned int fbSessionGetGroupOffset(
951     fbSession_t     *session,
952     char            *group)
953 {
954     unsigned int loop;
955 
956     for (loop = 0; loop < session->num_groups; loop++){
957         if (strcmp(group, session->all_groups[loop].name) == 0) {
958             return (loop + 1);
959         }
960     }
961 
962 
963     return 0;
964 }
965 
fbSessionSetPrivateGroup(fbSession_t * session,char * group,char * privgroup)966 void            fbSessionSetPrivateGroup(
967     fbSession_t      *session,
968     char             *group,
969     char             *privgroup)
970 {
971     unsigned int loop, group_offset = 0;
972     char **g;
973     GError *err = NULL;
974 
975     if (group == NULL || privgroup == NULL) {
976         return;
977     }
978 
979     for (loop = 0; loop < session->num_groups; loop++) {
980         if (strncmp(group, session->all_groups[loop].name,
981                     strlen(session->all_groups[loop].name)) == 0)
982         {
983             group_offset = loop + 1;
984         }
985     }
986 
987     if (group_offset == 0){
988         g_warning("Private Group requesting membership from UNKNOWN group");
989         return;
990     }
991 
992     if (fBufGetExporter(session->tdyn_buf) && session->group > 0) {
993         if (!fBufEmit(session->tdyn_buf, &err)) {
994             g_warning("Could not emit buffer %s", err->message);
995             g_clear_error(&err);
996         }
997     }
998 
999     /*Update external template table; create if necessary. */
1000 
1001     FB_SPREAD_MUTEX_LOCK(session);
1002     session->ext_ttab = g_hash_table_lookup(session->grp_ttab,
1003                                             GUINT_TO_POINTER(group_offset));
1004 
1005     if (!session->ext_ttab) {
1006         session->ext_ttab = g_hash_table_new(g_direct_hash, g_direct_equal);
1007         g_hash_table_insert(session->grp_ttab, GUINT_TO_POINTER(group_offset),
1008                             session->ext_ttab);
1009     }
1010     FB_SPREAD_MUTEX_UNLOCK(session);
1011 
1012     g_hash_table_insert(session->grp_seqtab, GUINT_TO_POINTER(session->group),
1013                         GUINT_TO_POINTER(session->sequence));
1014 
1015     /* Get new sequence number */
1016     session->sequence = GPOINTER_TO_UINT(
1017         g_hash_table_lookup(session->grp_seqtab,
1018                             GUINT_TO_POINTER(group_offset)));
1019 
1020     session->group = group_offset;
1021 
1022     g = &privgroup;
1023 
1024     if (fBufGetExporter(session->tdyn_buf)) {
1025         fBufSetExportGroups(session->tdyn_buf, g, 1, NULL);
1026     }
1027 
1028     if (!fbSessionExportTemplates(session, &err)) {
1029         g_clear_error(&err);
1030     }
1031 }
1032 
1033 
1034 /**
1035  *  fbSessionSpreadAddTemplatesHelper
1036  *
1037  *    Helper function for fbSessionAddTemplatesMulticast() and
1038  *    fbSessionAddTemplatesMulticastWithMetadata().
1039  *
1040  *    FIXME: Maybe this code should just invoke
1041  *    fbSessionAddTemplateHelper() when `internal` is TRUE?
1042  */
fbSessionSpreadAddTemplatesHelper(fbSession_t * session,char ** groups,gboolean internal,uint16_t tid,fbTemplate_t * tmpl,char * name,char * description,GError ** err)1043 static uint16_t fbSessionSpreadAddTemplatesHelper(
1044     fbSession_t      *session,
1045     char             **groups,
1046     gboolean         internal,
1047     uint16_t         tid,
1048     fbTemplate_t     *tmpl,
1049     char             *name,
1050     char             *description,
1051     GError           **err)
1052 {
1053     int n;
1054     unsigned int group_offset;
1055     GHashTable *ttab;
1056 
1057     g_assert(tmpl);
1058     g_assert(tid == FB_TID_AUTO || tid >= FB_TID_MIN_DATA);
1059     g_assert(groups);
1060     g_assert(err);
1061     if (groups == NULL) {
1062         g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_SETUP,
1063                     "No groups provided");
1064         return 0;
1065     }
1066 
1067     if (fBufGetExporter(session->tdyn_buf) && session->group > 0) {
1068         /* we are now going to multicast tmpls so we need to emit
1069            records currently in the buffer */
1070         if (!fBufEmit(session->tdyn_buf, err)) {
1071             return 0;
1072         }
1073     }
1074 
1075     /* Select a template table to add the template to */
1076     ttab = internal ? session->int_ttab : session->ext_ttab;
1077 
1078     /* Handle an auto-template-id or an illegal ID */
1079     if (tid < FB_TID_MIN_DATA) {
1080         if (tid != FB_TID_AUTO) {
1081             g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_TMPL,
1082                         "Illegal template id %d", tid);
1083             return 0;
1084         }
1085         tid = fbSessionFindUnusedTemplateId(session, internal);
1086         if (0 == tid) {
1087             g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_TMPL,
1088                         "Template table is full, no IDs left");
1089             return 0;
1090         }
1091     }
1092 
1093     /* Update external template table per group; create if necessary. */
1094     for (n = 0; groups[n] != NULL; ++n) {
1095         group_offset = fbSessionGetGroupOffset(session, groups[n]);
1096 
1097         if (group_offset == 0) {
1098             g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_SETUP,
1099                         "Spread Group Not Recognized.");
1100             return 0;
1101         }
1102 
1103         FB_SPREAD_MUTEX_LOCK(session);
1104         session->ext_ttab = g_hash_table_lookup(session->grp_ttab,
1105                                                GUINT_TO_POINTER(group_offset));
1106 
1107         if (!session->ext_ttab) {
1108             session->ext_ttab = g_hash_table_new(g_direct_hash,g_direct_equal);
1109             g_hash_table_insert(session->grp_ttab,
1110                                 GUINT_TO_POINTER(group_offset),
1111                                 session->ext_ttab);
1112         }
1113 
1114         FB_SPREAD_MUTEX_UNLOCK(session);
1115         g_hash_table_insert(session->grp_seqtab,
1116                             GUINT_TO_POINTER(session->group),
1117                             GUINT_TO_POINTER(session->sequence));
1118 
1119         /* Get new sequence number */
1120         session->sequence = GPOINTER_TO_UINT(
1121             g_hash_table_lookup(session->grp_seqtab,
1122                                 GUINT_TO_POINTER(group_offset)));
1123 
1124         /* keep new group */
1125         session->group = group_offset;
1126 
1127         /* Revoke old template, ignoring missing template error. */
1128         if (!fbSessionRemoveTemplate(session, internal, tid, err)) {
1129             if (g_error_matches(*err, FB_ERROR_DOMAIN, FB_ERROR_TMPL)) {
1130                 g_clear_error(err);
1131             } else {
1132                 return 0;
1133             }
1134         }
1135 
1136         /* Insert template into table */
1137         if (!internal)
1138             FB_SPREAD_MUTEX_LOCK(session);
1139 
1140         g_hash_table_insert(ttab, GUINT_TO_POINTER((unsigned int)tid), tmpl);
1141 
1142         if (!internal)
1143             FB_SPREAD_MUTEX_UNLOCK(session);
1144 
1145         fbTemplateRetain(tmpl);
1146 
1147         if (internal) {
1148             /* we don't really multicast internal tmpls - we only have
1149              * one internal tmpl table - so once is enough */
1150             return tid;
1151         }
1152     }
1153 
1154     /* Now set session to group 1 before we multicast */
1155     group_offset = fbSessionGetGroupOffset(session, groups[0]);
1156 
1157     FB_SPREAD_MUTEX_LOCK(session);
1158     session->ext_ttab = g_hash_table_lookup(session->grp_ttab,
1159                                             GUINT_TO_POINTER(group_offset));
1160     FB_SPREAD_MUTEX_UNLOCK(session);
1161 
1162     g_hash_table_insert(session->grp_seqtab, GUINT_TO_POINTER(session->group),
1163                         GUINT_TO_POINTER(session->sequence));
1164 
1165     /* Get sequence number - since it's the first group in list */
1166     session->sequence = GPOINTER_TO_UINT(
1167         g_hash_table_lookup(session->grp_seqtab,
1168                             GUINT_TO_POINTER(group_offset)));
1169 
1170     /* keep new group */
1171     session->group = group_offset;
1172 
1173     if (name && session->export_template_metadata) {
1174         fbTemplateAddMetadataRecord(tmpl, tid, name, description);
1175     }
1176 
1177     if (fBufGetExporter(session->tdyn_buf)) {
1178         fBufSetExportGroups(session->tdyn_buf, groups, n, NULL);
1179         if (name && !fbSessionWriteTemplateMetadata(session, tmpl, err)) {
1180             if (err && g_error_matches(*err, FB_ERROR_DOMAIN, FB_ERROR_TMPL)) {
1181                 g_clear_error(err);
1182             } else {
1183                 return 0;
1184             }
1185         }
1186         if (!fBufAppendTemplate(session->tdyn_buf, tid, tmpl, FALSE, err))
1187             return 0;
1188     }
1189 
1190     return tid;
1191 }
1192 
1193 
1194 /**
1195  * fbSessionAddTemplatesMulticast
1196  *
1197  *
1198  */
fbSessionAddTemplatesMulticast(fbSession_t * session,char ** groups,gboolean internal,uint16_t tid,fbTemplate_t * tmpl,GError ** err)1199 uint16_t        fbSessionAddTemplatesMulticast(
1200     fbSession_t      *session,
1201     char             **groups,
1202     gboolean         internal,
1203     uint16_t         tid,
1204     fbTemplate_t     *tmpl,
1205     GError           **err)
1206 {
1207     return fbSessionSpreadAddTemplatesHelper(
1208         session, groups, internal, tid, tmpl, NULL, NULL, err);
1209 }
1210 
1211 /**
1212  * fbSessionAddTemplatesMulticastWithMetadata
1213  *
1214  *
1215  */
fbSessionAddTemplatesMulticastWithMetadata(fbSession_t * session,char ** groups,gboolean internal,uint16_t tid,fbTemplate_t * tmpl,char * name,char * description,GError ** err)1216 uint16_t        fbSessionAddTemplatesMulticastWithMetadata(
1217     fbSession_t      *session,
1218     char             **groups,
1219     gboolean         internal,
1220     uint16_t         tid,
1221     fbTemplate_t     *tmpl,
1222     char             *name,
1223     char             *description,
1224     GError           **err)
1225 {
1226     if (!name) {
1227         g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_SETUP,
1228                     "Template name must be specified");
1229         return 0;
1230     }
1231     return fbSessionSpreadAddTemplatesHelper(
1232         session, groups, internal, tid, tmpl, name, description, err);
1233 }
1234 
1235 
1236 /**
1237  * fbSessionSetGroup
1238  *
1239  *
1240  */
fbSessionSetGroup(fbSession_t * session,char * group)1241 void            fbSessionSetGroup(
1242     fbSession_t      *session,
1243     char             *group)
1244 {
1245     unsigned int group_offset;
1246     GError *err = NULL;
1247 
1248     if (group == NULL && session->ext_ttab) {
1249         /* ext_ttab should already be setup and we are multicasting
1250            so no need to setup any tables */
1251         return;
1252     }
1253 
1254     group_offset = fbSessionGetGroupOffset(session, group);
1255 
1256     if (group_offset == 0) {
1257         g_warning("Spread Group Not Recognized.");
1258         return;
1259     }
1260 
1261     /* short-circut identical group if not initializing */
1262     if (session->ext_ttab && (session->group == group_offset))  return;
1263 
1264     if (fBufGetExporter(session->tdyn_buf) && session->group > 0) {
1265         /* Group is changing - meaning tmpls changing - emit now */
1266         if (!fBufEmit(session->tdyn_buf, &err)) {
1267             g_warning("Could not emit buffer before setting group: %s",
1268                       err->message);
1269             g_clear_error(&err);
1270         }
1271     }
1272     /*Update external template table; create if necessary. */
1273 
1274     if (fBufGetExporter(session->tdyn_buf)) {
1275         /* Only need to do this for exporters */
1276         /* Collector's templates aren't managed per group */
1277         FB_SPREAD_MUTEX_LOCK(session);
1278         session->ext_ttab = g_hash_table_lookup(session->grp_ttab,
1279                                                GUINT_TO_POINTER(group_offset));
1280 
1281         if (!session->ext_ttab) {
1282             session->ext_ttab =g_hash_table_new(g_direct_hash, g_direct_equal);
1283             g_hash_table_insert(session->grp_ttab,
1284                                 GUINT_TO_POINTER(group_offset),
1285                                 session->ext_ttab);
1286         }
1287 
1288         FB_SPREAD_MUTEX_UNLOCK(session);
1289     }
1290 
1291     g_hash_table_insert(session->grp_seqtab, GUINT_TO_POINTER(session->group),
1292                         GUINT_TO_POINTER(session->sequence));
1293 
1294     /* Get new sequence number */
1295     session->sequence = GPOINTER_TO_UINT(
1296         g_hash_table_lookup(session->grp_seqtab,
1297                             GUINT_TO_POINTER(group_offset)));
1298 
1299     /* keep new group */
1300     session->group = group_offset;
1301 
1302 }
1303 
fbSessionGetGroup(fbSession_t * session)1304 unsigned int    fbSessionGetGroup(
1305     fbSession_t *session)
1306 {
1307     return session->group;
1308 }
1309 
1310 #endif  /* HAVE_SPREAD */
1311 
1312 
fbSessionGetDomain(fbSession_t * session)1313 uint32_t        fbSessionGetDomain(
1314     fbSession_t     *session)
1315 {
1316     return session->domain;
1317 }
1318 
1319 
fbSessionAddTemplate(fbSession_t * session,gboolean internal,uint16_t tid,fbTemplate_t * tmpl,GError ** err)1320 uint16_t        fbSessionAddTemplate(
1321     fbSession_t     *session,
1322     gboolean        internal,
1323     uint16_t        tid,
1324     fbTemplate_t    *tmpl,
1325     GError          **err)
1326 {
1327     return fbSessionAddTemplateHelper(session, internal, tid, tmpl,
1328                                       NULL, NULL, err);
1329 }
1330 
1331 
1332 /**
1333  *    Helper function for fbSessionAddTemplate() and
1334  *    fbSessionAddTemplateWithMetadata().
1335  */
fbSessionAddTemplateHelper(fbSession_t * session,gboolean internal,uint16_t tid,fbTemplate_t * tmpl,const char * name,const char * description,GError ** err)1336 static uint16_t fbSessionAddTemplateHelper(
1337     fbSession_t         *session,
1338     gboolean             internal,
1339     uint16_t             tid,
1340     fbTemplate_t         *tmpl,
1341     const char           *name,
1342     const char           *description,
1343     GError               **err)
1344 {
1345     GHashTable *ttab;
1346 
1347     g_assert(tmpl);
1348     g_assert(tid == FB_TID_AUTO || tid >= FB_TID_MIN_DATA);
1349     g_assert(err);
1350 
1351     /* Select a template table to add the template to */
1352     ttab = internal ? session->int_ttab : session->ext_ttab;
1353 
1354     /* Handle an auto-template-id or an illegal ID */
1355     if (tid < FB_TID_MIN_DATA) {
1356         if (tid != FB_TID_AUTO) {
1357             g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_TMPL,
1358                         "Illegal template id %d", tid);
1359             return 0;
1360         }
1361         tid = fbSessionFindUnusedTemplateId(session, internal);
1362         if (0 == tid) {
1363             g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_TMPL,
1364                         "Template table is full, no IDs left");
1365             return 0;
1366         }
1367     }
1368 
1369     /* Revoke old template, ignoring missing template error. */
1370     if (!fbSessionRemoveTemplate(session, internal, tid, err)) {
1371         if (g_error_matches(*err, FB_ERROR_DOMAIN, FB_ERROR_TMPL)) {
1372             g_clear_error(err);
1373         } else {
1374             return 0;
1375         }
1376     }
1377 
1378     if (name && session->export_template_metadata) {
1379         fbTemplateAddMetadataRecord(tmpl, tid, name, description);
1380     }
1381 
1382     /* Write template to dynamics buffer */
1383     if (fBufGetExporter(session->tdyn_buf) && !internal) {
1384         if (name && !fbSessionWriteTemplateMetadata(session, tmpl, err)) {
1385             if (err && g_error_matches(*err, FB_ERROR_DOMAIN, FB_ERROR_TMPL)) {
1386                 g_clear_error(err);
1387             } else {
1388                 return 0;
1389             }
1390         }
1391         if (!fBufAppendTemplate(session->tdyn_buf, tid, tmpl, FALSE, err))
1392             return 0;
1393     }
1394 
1395     /* Insert template into table */
1396 #if HAVE_SPREAD
1397     if (!internal)
1398         FB_SPREAD_MUTEX_LOCK(session);
1399 #endif
1400     g_hash_table_insert(ttab, GUINT_TO_POINTER((unsigned int)tid), tmpl);
1401 
1402     if (internal &&
1403         tmpl->ie_internal_len > session->largestInternalTemplateLength)
1404     {
1405         session->largestInternalTemplate = tmpl;
1406         session->largestInternalTemplateLength = tmpl->ie_internal_len;
1407     }
1408 
1409     if (internal) {
1410         session->intTmplTableChanged = TRUE;
1411     } else {
1412         session->extTmplTableChanged = TRUE;
1413     }
1414 
1415 #if HAVE_SPREAD
1416     if (!internal)
1417         FB_SPREAD_MUTEX_UNLOCK(session);
1418 #endif
1419 
1420     fbTemplateRetain(tmpl);
1421 
1422     return tid;
1423 }
1424 
1425 
fbSessionRemoveTemplate(fbSession_t * session,gboolean internal,uint16_t tid,GError ** err)1426 gboolean        fbSessionRemoveTemplate(
1427     fbSession_t     *session,
1428     gboolean        internal,
1429     uint16_t        tid,
1430     GError          **err)
1431 {
1432     GHashTable      *ttab = NULL;
1433     fbTemplate_t    *tmpl = NULL;
1434     gboolean        ok = TRUE;
1435 
1436     /* Select a template table to remove the template from */
1437     ttab = internal ? session->int_ttab : session->ext_ttab;
1438 
1439     /* Get the template to remove */
1440     tmpl = fbSessionGetTemplate(session, internal, tid, err);
1441     if (!tmpl) return FALSE;
1442 
1443     /* Write template withdrawal to dynamics buffer */
1444     if (fBufGetExporter(session->tdyn_buf) && !internal) {
1445         ok = fBufAppendTemplate(session->tdyn_buf, tid, tmpl, TRUE, err);
1446     }
1447 
1448     /* Remove template */
1449 #if HAVE_SPREAD
1450     if (!internal)
1451         FB_SPREAD_MUTEX_LOCK(session);
1452 #endif
1453     g_hash_table_remove(ttab, GUINT_TO_POINTER((unsigned int)tid));
1454 
1455     if (internal) {
1456         session->intTmplTableChanged = TRUE;
1457     } else {
1458         session->extTmplTableChanged = TRUE;
1459     }
1460 
1461     fbSessionRemoveTemplatePair(session, tid);
1462 
1463     fBufRemoveTemplateTcplan(session->tdyn_buf, tmpl);
1464 
1465     if (internal && session->largestInternalTemplate == tmpl) {
1466         session->largestInternalTemplate = NULL;
1467         session->largestInternalTemplateLength = 0;
1468         fbSessionSetLargestInternalTemplateLen(session);
1469     }
1470 
1471 #if HAVE_SPREAD
1472     if (!internal)
1473         FB_SPREAD_MUTEX_UNLOCK(session);
1474 #endif
1475     fbTemplateRelease(tmpl);
1476 
1477     return ok;
1478 }
1479 
fbSessionGetTemplate(fbSession_t * session,gboolean internal,uint16_t tid,GError ** err)1480 fbTemplate_t    *fbSessionGetTemplate(
1481     fbSession_t     *session,
1482     gboolean        internal,
1483     uint16_t        tid,
1484     GError          **err)
1485 {
1486     GHashTable      *ttab;
1487     fbTemplate_t    *tmpl;
1488 
1489     /* Select a template table to get the template from */
1490     ttab = internal ? session->int_ttab : session->ext_ttab;
1491 
1492 #if HAVE_SPREAD
1493     if (!internal)
1494         FB_SPREAD_MUTEX_LOCK(session);
1495 #endif
1496     tmpl = g_hash_table_lookup(ttab, GUINT_TO_POINTER((unsigned int)tid));
1497 #if HAVE_SPREAD
1498     if (!internal)
1499         FB_SPREAD_MUTEX_UNLOCK(session);
1500 #endif
1501     /* Check for missing template */
1502     if (!tmpl) {
1503         if (internal) {
1504             g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_TMPL,
1505                         "Missing internal template %04hx",
1506                         tid);
1507         } else {
1508             g_set_error(err, FB_ERROR_DOMAIN, FB_ERROR_TMPL,
1509                         "Missing external template %08x:%04hx",
1510                         session->domain, tid);
1511         }
1512     }
1513 
1514     return tmpl;
1515 }
1516 
fbSessionExportTemplate(fbSession_t * session,uint16_t tid,GError ** err)1517 gboolean        fbSessionExportTemplate(
1518     fbSession_t     *session,
1519     uint16_t        tid,
1520     GError          **err)
1521 {
1522     fbTemplate_t    *tmpl = NULL;
1523     GError          *child_err = NULL;
1524 
1525     /* short-circuit on no template dynamics buffer */
1526     if (!fBufGetExporter(session->tdyn_buf))
1527         return TRUE;
1528 
1529     /* look up template */
1530     if (!(tmpl = fbSessionGetTemplate(session, FALSE, tid, err)))
1531         return FALSE;
1532 
1533     if (!fbSessionWriteTemplateMetadata(session, tmpl, &child_err)) {
1534         if (g_error_matches(child_err, FB_ERROR_DOMAIN, FB_ERROR_TMPL)) {
1535             g_clear_error(&child_err);
1536         } else {
1537             g_propagate_error(err, child_err);
1538             return FALSE;
1539         }
1540     }
1541 
1542     /* export it */
1543     return fBufAppendTemplate(session->tdyn_buf, tid, tmpl, FALSE, err);
1544 }
1545 
1546 
1547 /*
1548  *  Appends a single template metadata options record for 'tmpl' to
1549  *  the template dynamics buffer for 'session'.
1550  *
1551  *  This is a callback for g_hash_table_foreach() and is invoked by
1552  *  fbSessionExportTemplates().
1553  *
1554  *  This function assumes the internal and external template for the
1555  *  fBuf have been set to template_metadata_tid.
1556  */
fbSessionExportOneTemplateMetadataRecord(void * vtid,fbTemplate_t * tmpl,fbSession_t * session)1557 static void fbSessionExportOneTemplateMetadataRecord(
1558     void            *vtid,
1559     fbTemplate_t    *tmpl,
1560     fbSession_t     *session)
1561 {
1562     uint16_t        tid = (uint16_t)GPOINTER_TO_UINT(vtid);
1563 
1564     if (!tmpl->metadata_rec) {
1565         return;
1566     }
1567 
1568     /* The type/template metadata templates should have already been exported */
1569     if (tid == session->info_element_metadata_tid
1570         || tid == session->template_metadata_tid)
1571     {
1572         return;
1573     }
1574 
1575     if (fBufGetExporter(session->tdyn_buf) && !session->tdyn_err) {
1576         /* the internal and external template must be set on the fBuf
1577          * before using this function or everything will go to crap */
1578         if (!fBufAppend(session->tdyn_buf, (uint8_t *)tmpl->metadata_rec,
1579                         sizeof(fbTemplateOptRec_t), &session->tdyn_err))
1580         {
1581 #if FB_DEBUG_MD
1582             fprintf(stderr, "fBufAppend(%p) failed: %s\n",
1583                     (void *)tmpl->metadata_rec,
1584                     (session->tdyn_err ? session->tdyn_err->message : ""));
1585 #endif
1586             if (!session->tdyn_err) {
1587                 g_set_error(&session->tdyn_err, FB_ERROR_DOMAIN, FB_ERROR_IO,
1588                             "Unspecified template export error");
1589             }
1590         }
1591     }
1592 }
1593 
1594 
1595 /*
1596  *  Appends a single template record for 'tmpl' to the template
1597  *  dynamics buffer for 'session'.
1598  *
1599  *  This is a callback for g_hash_table_foreach() and is invoked by
1600  *  fbSessionExportTemplates().
1601  */
fbSessionExportOneTemplate(void * vtid,fbTemplate_t * tmpl,fbSession_t * session)1602 static void     fbSessionExportOneTemplate(
1603     void            *vtid,
1604     fbTemplate_t    *tmpl,
1605     fbSession_t     *session)
1606 {
1607     uint16_t        tid = (uint16_t)GPOINTER_TO_UINT(vtid);
1608 
1609     /* The type/template metadata templates should have already been exported */
1610     if (tid == session->info_element_metadata_tid
1611         || tid == session->template_metadata_tid)
1612     {
1613         return;
1614     }
1615 
1616     /* fprintf(stderr, "fbSessionExportOneTemplate(%#x, %p)\n", tid, tmpl); */
1617     if (fBufGetExporter(session->tdyn_buf) && !session->tdyn_err) {
1618         if (!fBufAppendTemplate(session->tdyn_buf, tid, tmpl,
1619                                 FALSE, &session->tdyn_err))
1620         {
1621             if (!session->tdyn_err) {
1622                 g_set_error(&session->tdyn_err, FB_ERROR_DOMAIN, FB_ERROR_IO,
1623                             "Unspecified template export error");
1624             }
1625         }
1626     }
1627 }
1628 
fbSessionExportTemplates(fbSession_t * session,GError ** err)1629 gboolean        fbSessionExportTemplates(
1630     fbSession_t     *session,
1631     GError          **err)
1632 {
1633     uint16_t int_tid;
1634     uint16_t ext_tid;
1635     GError *child_err = NULL;
1636     gboolean ret = TRUE;
1637 
1638     /* require an exporter */
1639     if (!fBufGetExporter(session->tdyn_buf))
1640         return TRUE;
1641 
1642     int_tid = fBufGetInternalTemplate(session->tdyn_buf);
1643     ext_tid = fBufGetExportTemplate(session->tdyn_buf);
1644 
1645     if (session->export_info_element_metadata) {
1646 #if FB_DEBUG_MD
1647         fprintf(stderr, "Exporting info element metadata; template %#x\n",
1648                 session->info_element_metadata_tid);
1649 #endif
1650         if (!fbSessionExportTemplate(
1651                 session, session->info_element_metadata_tid, err))
1652         {
1653 #if FB_DEBUG_MD
1654             fprintf(stderr, "fbSessionExportTemplate(%#x) failed: %s\n",
1655                     session->info_element_metadata_tid,
1656                     (err ? (*err)->message : ""));
1657 #endif
1658             ret = FALSE;
1659             goto END;
1660         }
1661         if (!fbSessionWriteTypeMetadata(session, err)) {
1662             ret = FALSE;
1663             goto END;
1664         }
1665     }
1666 
1667     if (session->export_template_metadata) {
1668 #if FB_DEBUG_MD
1669         fprintf(stderr, "Exporting template metadata; template %#x\n",
1670                 session->template_metadata_tid);
1671 #endif
1672         if (!fbSessionExportTemplate(
1673                 session, session->template_metadata_tid, err))
1674         {
1675 #if FB_DEBUG_MD
1676             fprintf(stderr, "fbSessionExportTemplate(%#x) failed: %s\n",
1677                     session->template_metadata_tid,
1678                     (err ? (*err)->message : ""));
1679 #endif
1680             ret = FALSE;
1681             goto END;
1682         }
1683         if (session->ext_ttab && fBufGetExporter(session->tdyn_buf)) {
1684             if (!fBufSetInternalTemplate(session->tdyn_buf,
1685                                          session->template_metadata_tid,
1686                                          &child_err))
1687             {
1688 #if FB_DEBUG_MD
1689                 fprintf(stderr, "fBufSetInternalTemplate(%#x) failed: %s\n",
1690                         session->template_metadata_tid, child_err->message);
1691 #endif
1692                 if (g_error_matches(child_err, FB_ERROR_DOMAIN,FB_ERROR_TMPL)){
1693                     g_clear_error(&child_err);
1694                 }
1695             } else if (!fBufSetExportTemplate(session->tdyn_buf,
1696                                               session->template_metadata_tid,
1697                                               &child_err))
1698             {
1699 #if FB_DEBUG_MD
1700                 fprintf(stderr, "fBufSetExportTemplate(%#x) failed: %s\n",
1701                         session->template_metadata_tid, child_err->message);
1702 #endif
1703                 if (g_error_matches(child_err, FB_ERROR_DOMAIN,FB_ERROR_TMPL)){
1704                     g_clear_error(&child_err);
1705                 }
1706             } else {
1707                 FB_SPREAD_MUTEX_LOCK(session);
1708                 /* the fbufsetinternal/fbufsetexport template functions
1709                  * can't occur in the GHFunc since pthread_mutex_lock has
1710                  * already been called and fBufSetExportTemplate will call
1711                  * fbSessionGetTemplate which will try to acquire lock */
1712                 g_clear_error(&session->tdyn_err);
1713                 g_hash_table_foreach(
1714                     session->ext_ttab,
1715                     (GHFunc)fbSessionExportOneTemplateMetadataRecord, session);
1716                 if (session->tdyn_err) {
1717                     g_propagate_error(&child_err, session->tdyn_err);
1718                     session->tdyn_err = NULL;
1719                 }
1720                 FB_SPREAD_MUTEX_UNLOCK(session);
1721             }
1722             if (child_err) {
1723                 g_propagate_error(err, child_err);
1724                 child_err = NULL;
1725                 ret = FALSE;
1726                 goto END;
1727             }
1728         }
1729     }
1730 
1731     FB_SPREAD_MUTEX_LOCK(session);
1732     if (session->ext_ttab) {
1733         g_clear_error(&session->tdyn_err);
1734         g_hash_table_foreach(session->ext_ttab,
1735                              (GHFunc)fbSessionExportOneTemplate, session);
1736         if (session->tdyn_err) {
1737             g_propagate_error(err, session->tdyn_err);
1738             session->tdyn_err = NULL;
1739             ret = FALSE;
1740         }
1741     }
1742     FB_SPREAD_MUTEX_UNLOCK(session);
1743 
1744   END:
1745     if (int_tid
1746         && !fBufSetInternalTemplate(session->tdyn_buf, int_tid, &child_err))
1747     {
1748 #if FB_DEBUG_MD
1749         fprintf(stderr, "restore fBufSetInternalTemplate(%#x) failed: %s\n",
1750                 int_tid, child_err->message);
1751 #endif
1752         g_clear_error(&child_err);
1753     }
1754     if (ext_tid
1755         && !fBufSetExportTemplate(session->tdyn_buf, ext_tid, &child_err))
1756     {
1757 #if FB_DEBUG_MD
1758         fprintf(stderr, "restore fBufSetExportTemplate(%#x) failed: %s\n",
1759                 ext_tid, child_err->message);
1760 #endif
1761         g_clear_error(&child_err);
1762     }
1763     return ret;
1764 }
1765 
fbSessionCloneOneTemplate(void * vtid,fbTemplate_t * tmpl,fbSession_t * session)1766 static void     fbSessionCloneOneTemplate(
1767     void            *vtid,
1768     fbTemplate_t    *tmpl,
1769     fbSession_t     *session)
1770 {
1771     uint16_t        tid = (uint16_t)GPOINTER_TO_UINT(vtid);
1772     GError          *err = NULL;
1773 
1774     if (!fbSessionAddTemplateHelper(session, TRUE, tid, tmpl, NULL,NULL,&err)) {
1775         g_warning("Session clone internal template copy failed: %s",
1776                   err->message);
1777         g_clear_error(&err);
1778     }
1779 }
1780 
fbSessionClone(fbSession_t * base)1781 fbSession_t     *fbSessionClone(
1782     fbSession_t     *base)
1783 {
1784     fbSession_t     *session = NULL;
1785 
1786     /* Create a new session using the information model from the base */
1787     session = fbSessionAlloc(base->model);
1788 
1789     /* Add each internal template from the base session to the new session */
1790     g_hash_table_foreach(base->int_ttab,
1791                          (GHFunc)fbSessionCloneOneTemplate, session);
1792 
1793     /* Need to copy over callbacks because in the UDP case we won't have
1794        access to the session until after we call fBufNext and by that
1795        time it's too late and we've already missed some templates */
1796     session->new_template_callback = base->new_template_callback;
1797     session->tmpl_app_ctx = base->tmpl_app_ctx;
1798 
1799     /* copy collector reference */
1800     session->collector = base->collector;
1801 
1802     /* Return the new session */
1803     return session;
1804 }
1805 
fbSessionGetSequence(fbSession_t * session)1806 uint32_t        fbSessionGetSequence(
1807     fbSession_t     *session)
1808 {
1809     return session->sequence;
1810 }
1811 
fbSessionSetSequence(fbSession_t * session,uint32_t sequence)1812 void            fbSessionSetSequence(
1813     fbSession_t     *session,
1814     uint32_t        sequence)
1815 {
1816     session->sequence = sequence;
1817 }
1818 
fbSessionSetTemplateBuffer(fbSession_t * session,fBuf_t * fbuf)1819 void            fbSessionSetTemplateBuffer(
1820     fbSession_t     *session,
1821     fBuf_t          *fbuf)
1822 {
1823     session->tdyn_buf = fbuf;
1824 }
1825 
fbSessionGetInfoModel(fbSession_t * session)1826 fbInfoModel_t       *fbSessionGetInfoModel(
1827     fbSession_t         *session)
1828 {
1829     return session->model;
1830 }
1831 
fbSessionClearIntTmplTableFlag(fbSession_t * session)1832 void fbSessionClearIntTmplTableFlag(
1833     fbSession_t        *session)
1834 {
1835     session->intTmplTableChanged = FALSE;
1836 }
1837 
fbSessionClearExtTmplTableFlag(fbSession_t * session)1838 void fbSessionClearExtTmplTableFlag(
1839     fbSession_t        *session)
1840 {
1841     session->extTmplTableChanged = FALSE;
1842 }
1843 
fbSessionIntTmplTableFlagIsSet(fbSession_t * session)1844 int fbSessionIntTmplTableFlagIsSet(
1845     fbSession_t        *session)
1846 {
1847     return session->intTmplTableChanged;
1848 }
1849 
fbSessionExtTmplTableFlagIsSet(fbSession_t * session)1850 int fbSessionExtTmplTableFlagIsSet(
1851     fbSession_t        *session)
1852 {
1853     return session->extTmplTableChanged;
1854 }
1855 
fbSessionSetCollector(fbSession_t * session,fbCollector_t * collector)1856 void fbSessionSetCollector(
1857     fbSession_t        *session,
1858     fbCollector_t      *collector)
1859 {
1860     session->collector = collector;
1861 }
1862 
fbSessionGetCollector(fbSession_t * session)1863 fbCollector_t *fbSessionGetCollector(
1864     fbSession_t        *session)
1865 {
1866     return session->collector;
1867 }
1868 
fbSessionGetLargestInternalTemplateSize(fbSession_t * session)1869 uint16_t            fbSessionGetLargestInternalTemplateSize(
1870     fbSession_t        *session)
1871 {
1872     if (!session->largestInternalTemplateLength) {
1873         fbSessionSetLargestInternalTemplateLen(session);
1874     }
1875 
1876     return session->largestInternalTemplateLength;
1877 }
1878 
1879 /* Callback function used when scanning the hash table of internal
1880  * templates. */
fbSessionCheckTmplLengthForMax(gpointer key_in,gpointer value_in,gpointer user_value)1881 static void fbSessionCheckTmplLengthForMax(
1882     gpointer    key_in,
1883     gpointer    value_in,
1884     gpointer    user_value)
1885 {
1886     /* uint16_t        tid     = GPOINTER_TO_UINT(key_in); */
1887     fbTemplate_t   *tmpl    = (fbTemplate_t*)value_in;
1888     fbSession_t    *session = (fbSession_t*)user_value;
1889     (void)key_in;
1890 
1891     if (tmpl->ie_internal_len > session->largestInternalTemplateLength) {
1892         session->largestInternalTemplateLength  = tmpl->ie_internal_len;
1893         session->largestInternalTemplate        = tmpl;
1894     }
1895 }
1896 
fbSessionSetLargestInternalTemplateLen(fbSession_t * session)1897 static void fbSessionSetLargestInternalTemplateLen(
1898     fbSession_t    *session)
1899 {
1900     if (!session || !session->int_ttab) {
1901         return;
1902     }
1903     g_hash_table_foreach(session->int_ttab, fbSessionCheckTmplLengthForMax,
1904                          session);
1905 }
1906 
1907 
1908