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