This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
stellar-stellar/deps/utable/utable_ipfix_exporter.c
2024-11-25 19:22:19 +08:00

795 lines
27 KiB
C

#include <stdio.h>
#include <limits.h>
#include <stdint.h>
#include <stddef.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include "utable.h"
#include "cjson/cJSON.h"
#include "yyjson/yyjson.h"
#include "uthash/utarray.h"
#include "uthash/uthash.h"
#define IPFIX_DEFUALT_VERSION 10
#define GEEDGE_NETWORKS_PEN_NUMBER 54450
#define IPFIX_DEFUALT_PEN_NUMBER GEEDGE_NETWORKS_PEN_NUMBER
#define IPFIX_NONE_PEN_NUMBER -1
#define IPFIX_TEMPLATE_SET_ID 2
#define IPFIX_BUFF_MAX_SIZE 2048
#define IPFIX_ELEMENT_MAX_LEN 32
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
enum ipfix_type
{
IPFIX_UNSIGNED8 = 0,
IPFIX_UNSIGNED16,
IPFIX_UNSIGNED32,
IPFIX_UNSIGNED64,
IPFIX_VARIABLE_STRING,
IPFIX_UNKNOWN
};
struct ipfix_element
{
enum ipfix_type type;
uint16_t element_id;
uint16_t value_length; // if type is variable_string, value_length is 0
int PEN_number;
char name[IPFIX_ELEMENT_MAX_LEN];
};
struct ipfix_template
{
uint16_t template_id;
char *name;
UT_array *elements_array; // utarray for current template elements
};
struct ipfix_worker_context
{
int source_id;
int sequence;
size_t template_blob_length;
char *template_blob;
};
struct ipfix_exporter_schema
{
uint16_t n_worker;
uint16_t version;
int domain_id;
int PEN_number;
uint32_t templates_refresh_interval_s;
UT_array *templates_array; // utarray for templates
struct ipfix_worker_context *worker_context_array; // utarray for worker_context
};
struct ipfix_message_head
{
uint16_t version;
uint16_t length;
int exporttime;
int ipfix_message_sequence;
int domain_id;
};
#define IPFIX_MESSAGE_HEAD_LEN sizeof(struct ipfix_message_head)
// from maat
int load_file_to_memory(const char *file_name, unsigned char **pp_out, size_t *out_sz)
{
int ret = 0;
FILE *fp = NULL;
struct stat fstat_buf;
size_t read_size = 0;
ret = stat(file_name, &fstat_buf);
if (ret != 0)
{
return -1;
}
fp = fopen(file_name, "r");
if (fp == NULL)
{
return -1;
}
*out_sz = fstat_buf.st_size;
*pp_out = (unsigned char *)calloc(*out_sz + 1, sizeof(unsigned char));
read_size = fread(*pp_out, 1, *out_sz, fp);
if (read_size != *out_sz)
{
free(*pp_out);
*pp_out = NULL;
}
fclose(fp);
fp = NULL;
return 0;
}
int ipfix_exporter_get_type(struct ipfix_element *p_element, char *element_type)
{
if (strcmp(element_type, "string") == 0)
{
p_element->type = IPFIX_VARIABLE_STRING;
p_element->value_length = 0;
return 0;
}
else if (strcmp(element_type, "unsigned8") == 0)
{
p_element->type = IPFIX_UNSIGNED8;
p_element->value_length = sizeof(uint8_t);
return 0;
}
else if (strcmp(element_type, "unsigned16") == 0)
{
p_element->type = IPFIX_UNSIGNED16;
p_element->value_length = sizeof(uint16_t);
return 0;
}
else if (strcmp(element_type, "unsigned32") == 0)
{
p_element->type = IPFIX_UNSIGNED32;
p_element->value_length = sizeof(uint32_t);
return 0;
}
else if (strcmp(element_type, "unsigned64") == 0)
{
p_element->type = IPFIX_UNSIGNED64;
p_element->value_length = sizeof(uint64_t);
return 0;
}
else
{
return -1;
}
}
int ipfix_template_payload_init(UT_array *templates_array, char **blob, size_t *blob_len)
{
if (templates_array == NULL || utarray_len(templates_array) == 0 || blob == NULL || blob_len == NULL)
{
return -1;
}
size_t n_templates = utarray_len(templates_array);
size_t template_blob_len = 0;
for (size_t i = 0; i < n_templates; i++)
{
struct ipfix_template *template_item = (struct ipfix_template *)utarray_eltptr(templates_array, i);
size_t n_elements = utarray_len(template_item->elements_array);
template_blob_len += (sizeof(uint16_t) + sizeof(uint16_t) + n_elements * (sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t))); // template_id + field_count + element_id + length + PEN_number
}
template_blob_len += ((sizeof(uint16_t) + sizeof(uint16_t)) + IPFIX_MESSAGE_HEAD_LEN); // set_id + set_length + ipfix_message_head
char *payload = (char *)calloc(template_blob_len, sizeof(char));
int offset = 0;
offset += IPFIX_MESSAGE_HEAD_LEN; // skip ipfix_message_head
*(uint16_t *)(payload + offset) = htons(IPFIX_TEMPLATE_SET_ID); // set_id
offset += sizeof(uint16_t); // skip set_id
*(uint16_t *)(payload + offset) = htons(template_blob_len - IPFIX_MESSAGE_HEAD_LEN); // set_length
offset += sizeof(uint16_t); // skip set_length
for (size_t i = 0; i < n_templates; i++)
{
struct ipfix_template *template_item = (struct ipfix_template *)utarray_eltptr(templates_array, i);
size_t n_elements = utarray_len(template_item->elements_array);
*(uint16_t *)(payload + offset) = htons(template_item->template_id); // template_id
offset += sizeof(uint16_t); // skip template_id
*(uint16_t *)(payload + offset) = htons(n_elements); // field_count
offset += sizeof(uint16_t); // skip field_count
for (size_t j = 0; j < n_elements; j++)
{
struct ipfix_element *p_element = (struct ipfix_element *)utarray_eltptr(template_item->elements_array, j);
// set element_id
if (p_element->PEN_number != IPFIX_NONE_PEN_NUMBER)
{
*(uint16_t *)(payload + offset) = htons(p_element->element_id | 0x8000); // Pen_provided is yes
}
else
{
*(uint16_t *)(payload + offset) = htons(p_element->element_id); // Pen_provided is no
}
offset += sizeof(uint16_t); // skip element_id
// set element_length
if (p_element->type == IPFIX_VARIABLE_STRING)
{
*(uint16_t *)(payload + offset) = htons(65535); // if element_length is 65535, it means variable length
}
else
{
*(uint16_t *)(payload + offset) = htons(p_element->value_length); // length
}
offset += sizeof(uint16_t); // skip length
// set PEN_number
if (p_element->PEN_number != IPFIX_NONE_PEN_NUMBER)
{
*(uint32_t *)(payload + offset) = htonl(p_element->PEN_number); // PEN_number
offset += sizeof(uint32_t); // skip PEN_number
}
}
}
if (offset != (int)template_blob_len)
{
free(payload);
payload = NULL;
return -1;
}
*blob = payload;
*blob_len = offset;
return 0;
}
void ipfix_template_item_destroy(void *elt)
{
struct ipfix_template *template_item = (struct ipfix_template *)elt;
if (template_item == NULL)
{
return;
}
if (template_item->elements_array != NULL)
{
utarray_free(template_item->elements_array);
}
if (template_item->name != NULL)
{
free(template_item->name);
template_item->name = NULL;
}
return;
}
void ipfix_utarray_init(UT_array **_array, size_t sz_item, void (*dtor)(void *))
{
if (_array == NULL || *_array != NULL)
{
return;
}
UT_icd icd = {0};
icd.sz = sz_item;
icd.dtor = dtor;
utarray_new(*_array, &icd);
return;
}
UT_array *ipfix_elements_array_init(cJSON *root, const char *element_key, int PEN_number_global)
{
if (root == NULL || element_key == NULL)
{
return NULL;
}
cJSON *elements = cJSON_GetObjectItem(root, element_key);
if (elements == NULL)
{
return NULL;
}
UT_array *elements_array = NULL;
ipfix_utarray_init(&elements_array, sizeof(struct ipfix_element), NULL);
size_t n_elements = cJSON_GetArraySize(elements);
for (size_t i = 0; i < n_elements; i++)
{
struct ipfix_element element_item = {0};
cJSON *element = cJSON_GetArrayItem(elements, i);
cJSON *element_id = cJSON_GetObjectItem(element, "element_id");
if (element_id == NULL || element_id->valueint == 0) // element_id不存在、类型不为Number、值为0
{
goto error;
}
element_item.element_id = element_id->valueint;
cJSON *element_type = cJSON_GetObjectItem(element, "element_type");
if (element_type == NULL || element_type->valuestring == NULL) // element_type不存在、类型不为String、值为NULL
{
goto error;
}
if (ipfix_exporter_get_type(&element_item, element_type->valuestring) == -1) // element_type不在支持范围内
{
goto error;
}
cJSON *PEN_number = cJSON_GetObjectItem(element, "PEN_number");
if (PEN_number == NULL) // PEN_number不存在、类型不为Number
{
element_item.PEN_number = PEN_number_global;
}
else
{
element_item.PEN_number = PEN_number->valueint;
}
cJSON *element_name = cJSON_GetObjectItem(element, "element_name");
if (element_name == NULL || element_name->valuestring == NULL) // element_name不存在、类型不为String、值为NULL
{
goto error;
}
memcpy(element_item.name, element_name->valuestring, MIN(strlen(element_name->valuestring), IPFIX_ELEMENT_MAX_LEN - 1));
utarray_push_back(elements_array, &element_item);
}
return elements_array;
error:
utarray_free(elements_array);
elements_array = NULL;
return NULL;
}
void utable_ipfix_exporter_schema_free(struct ipfix_exporter_schema *instance)
{
if (instance == NULL)
{
return;
}
if (instance->templates_array)
{
utarray_free(instance->templates_array);
instance->templates_array = NULL;
}
if (instance->worker_context_array)
{
for (size_t i = 0; i < instance->n_worker; i++)
{
if (instance->worker_context_array[i].template_blob)
{
free(instance->worker_context_array[i].template_blob);
instance->worker_context_array[i].template_blob = NULL;
}
}
}
free(instance->worker_context_array);
instance->worker_context_array = NULL;
free(instance);
instance = NULL;
return;
}
struct ipfix_elements_hash
{
char *name;
UT_array *elements_array;
UT_hash_handle hh;
};
void ipfix_elements_array_hash_destroy(struct ipfix_elements_hash *elements_array_hash)
{
if (elements_array_hash == NULL)
{
return;
}
struct ipfix_elements_hash *elements_array_item = NULL;
struct ipfix_elements_hash *tmp = NULL;
HASH_ITER(hh, elements_array_hash, elements_array_item, tmp)
{
HASH_DEL(elements_array_hash, elements_array_item);
if (elements_array_item->name != NULL)
{
free(elements_array_item->name);
elements_array_item->name = NULL;
}
if (elements_array_item->elements_array != NULL)
{
utarray_free(elements_array_item->elements_array);
elements_array_item->elements_array = NULL;
}
free(elements_array_item);
elements_array_item = NULL;
}
return;
}
struct ipfix_exporter_schema *utable_ipfix_exporter_schema_new(const char *ipfix_schema_json_path, uint16_t domain_id, uint16_t n_worker)
{
if (ipfix_schema_json_path == NULL)
{
return NULL;
}
size_t json_size = 0;
unsigned char *json_str = NULL;
int ret = load_file_to_memory(ipfix_schema_json_path, &json_str, &json_size);
if (ret == -1)
{
return NULL;
}
cJSON *root = NULL;
root = cJSON_Parse((const char *)json_str);
if (root == NULL)
{
free(json_str);
json_str = NULL;
return NULL;
}
int version = 0;
version = cJSON_GetObjectItem(root, "version")->valueint;
if (version < IPFIX_DEFUALT_VERSION)
{
version = IPFIX_DEFUALT_VERSION;
}
int PEN_number = 0;
PEN_number = cJSON_GetObjectItem(root, "PEN_number")->valueint;
if (PEN_number <= 0)
{
PEN_number = IPFIX_DEFUALT_PEN_NUMBER;
}
uint32_t refresh_interval_s = 0;
refresh_interval_s = cJSON_GetObjectItem(root, "refresh_interval_s")->valueint;
if ((int)refresh_interval_s <= 0)
{
refresh_interval_s = 60;
}
size_t n_template = 0;
cJSON *templates = NULL;
templates = cJSON_GetObjectItem(root, "templates");
if (templates == NULL || cJSON_GetArraySize(templates) == 0)
{
free(json_str);
json_str = NULL;
return NULL;
}
n_template = cJSON_GetArraySize(templates);
UT_array *templates_array = NULL;
ipfix_utarray_init(&templates_array, sizeof(struct ipfix_template), ipfix_template_item_destroy);
struct ipfix_elements_hash *elements_array_hash = NULL;
for (size_t i = 0; i < n_template; i++)
{
struct ipfix_template template_item = {0};
cJSON *template_obj = cJSON_GetArrayItem(templates, i);
cJSON *template_id = cJSON_GetObjectItem(template_obj, "template_id");
if (template_id == NULL || template_id->valueint < 256 || template_id->valueint == 0xffff)
{
goto error;
}
template_item.template_id = template_id->valueint;
cJSON *template_name = cJSON_GetObjectItem(template_obj, "template_name");
if (template_name == NULL || template_name->valuestring == NULL)
{
goto error;
}
template_item.name = (char *)calloc(strlen(template_name->valuestring) + 1, sizeof(char));
memcpy(template_item.name, template_name->valuestring, strlen(template_name->valuestring));
cJSON *elements_key_array = cJSON_GetObjectItem(template_obj, "elements");
if (elements_key_array == NULL)
{
goto error;
}
size_t n_elements_key = cJSON_GetArraySize(elements_key_array);
ipfix_utarray_init(&template_item.elements_array, sizeof(struct ipfix_element), NULL);
for (size_t j = 0; j < n_elements_key; j++)
{
cJSON *element_key = cJSON_GetArrayItem(elements_key_array, j);
struct ipfix_elements_hash *elements_array_item = NULL;
HASH_FIND_STR(elements_array_hash, element_key->valuestring, elements_array_item);
if (elements_array_item == NULL)
{
elements_array_item = (struct ipfix_elements_hash *)calloc(1, sizeof(struct ipfix_elements_hash));
elements_array_item->name = (char *)calloc(strlen(element_key->valuestring) + 1, sizeof(char));
memcpy(elements_array_item->name, element_key->valuestring, strlen(element_key->valuestring));
HASH_ADD_STR(elements_array_hash, name, elements_array_item);
elements_array_item->elements_array = ipfix_elements_array_init(root, element_key->valuestring, PEN_number);
}
if (elements_array_item->elements_array == NULL)
{
goto error;
}
utarray_concat(template_item.elements_array, elements_array_item->elements_array); // merge elements_array
}
utarray_push_back(templates_array, &template_item);
}
ipfix_elements_array_hash_destroy(elements_array_hash);
size_t sz_blob = 0;
char *template_blob = NULL;
ret = ipfix_template_payload_init(templates_array, &template_blob, &sz_blob);
if (ret == -1 || sz_blob == 0 || template_blob == NULL)
{
goto error;
}
struct ipfix_worker_context *worker_context_array = (struct ipfix_worker_context *)calloc(n_worker, sizeof(struct ipfix_worker_context));
for (size_t i = 0; i < n_worker; i++)
{
worker_context_array[i].source_id = (domain_id << 16) | i;
worker_context_array[i].sequence = 0;
worker_context_array[i].template_blob_length = sz_blob;
worker_context_array[i].template_blob = (char *)calloc(sz_blob, sizeof(char));
memcpy(worker_context_array[i].template_blob, template_blob, sz_blob);
struct ipfix_message_head *p_message_head = (struct ipfix_message_head *)(worker_context_array[i].template_blob);
p_message_head->version = htons(version);
p_message_head->length = htons(sz_blob);
p_message_head->domain_id = htonl(worker_context_array[i].source_id);
}
struct ipfix_exporter_schema *_instance = (struct ipfix_exporter_schema *)calloc(1, sizeof(struct ipfix_exporter_schema));
_instance->version = version;
_instance->PEN_number = PEN_number;
_instance->templates_refresh_interval_s = refresh_interval_s;
_instance->domain_id = domain_id;
_instance->n_worker = n_worker;
_instance->templates_array = templates_array;
_instance->worker_context_array = worker_context_array;
free(template_blob);
cJSON_Delete(root);
free(json_str);
json_str = NULL;
return _instance;
error:
utarray_free(templates_array);
cJSON_Delete(root);
free(json_str);
json_str = NULL;
return NULL;
}
int utable_ipfix_template_get(struct ipfix_exporter_schema *instance, const char *template_name)
{
if (instance == NULL || template_name == NULL)
{
return -1;
}
size_t n_template = utarray_len(instance->templates_array);
for (size_t i = 0; i < n_template; i++)
{
struct ipfix_template *template_item = (struct ipfix_template *)utarray_eltptr(instance->templates_array, i);
if (strcmp(template_item->name, template_name) == 0)
{
return i;
}
}
return -1;
}
char *ipfix_data_interger_serialize(char *buff, size_t *offset, size_t *buff_sz, int64_t value, size_t sz_value)
{
if (*offset + sz_value >= *buff_sz)
{
char *new_buff = (char *)realloc(buff, *offset + sz_value + IPFIX_BUFF_MAX_SIZE);
if (new_buff == NULL)
{
free(buff);
return NULL;
}
buff = new_buff;
*buff_sz = *offset + sz_value + IPFIX_BUFF_MAX_SIZE;
}
switch (sz_value)
{
case 1:
*(uint8_t *)(buff + *offset) = (uint8_t)value;
*offset += 1;
break;
case 2:
*(uint16_t *)(buff + *offset) = htons((uint16_t)value);
*offset += 2;
break;
case 4:
*(uint32_t *)(buff + *offset) = htonl((uint32_t)value);
*offset += 4;
break;
case 8:
*(uint64_t *)(buff + *offset) = htobe64((uint64_t)value);
*offset += 8;
break;
}
return buff;
}
char *ipfix_data_variable_serialize(char *buff, size_t *offset, size_t *buff_sz, char *value, size_t sz_value)
{
if (*offset + sz_value + 3 >= *buff_sz) // 3 means variable_string length > 255, need 3 bytes to describe length
{
char *new_buff = (char *)realloc(buff, *offset + sz_value + IPFIX_BUFF_MAX_SIZE);
if (new_buff == NULL)
{
free(buff);
return NULL;
}
buff = new_buff;
*buff_sz = *offset + sz_value + IPFIX_BUFF_MAX_SIZE;
}
if (sz_value == 0 || value == NULL) // value == NULL need 1 byte to describe length
{
*(uint8_t *)(buff + *offset) = 0;
*offset += 1;
}
else if (sz_value < 255)
{
*(uint8_t *)(buff + *offset) = (uint8_t)sz_value;
*offset += 1;
memcpy(buff + *offset, value, sz_value);
*offset += sz_value;
}
else // >= 255
{
*(uint8_t *)(buff + *offset) = 255;
*offset += 1;
*(uint16_t *)(buff + *offset) = htons(sz_value);
*offset += 2;
memcpy(buff + *offset, value, sz_value);
*offset += sz_value;
}
return buff;
}
int utable_ipfix_data_flow_exporter(const struct utable *table, struct ipfix_exporter_schema *instance, int template_id, uint16_t worker_id, char **blob, size_t *blob_len)
{
if (table == NULL || instance == NULL || worker_id >= instance->n_worker || template_id < 0 || template_id >= (int)utarray_len(instance->templates_array))
{
return -1;
}
struct ipfix_template *template_item = (struct ipfix_template *)utarray_eltptr(instance->templates_array, (unsigned int)template_id);
if (template_item == NULL)
{
return -1;
}
size_t offset = 0;
size_t n_elements = utarray_len(template_item->elements_array);
size_t buff_sz = IPFIX_BUFF_MAX_SIZE;
char *buff = (char *)calloc(IPFIX_BUFF_MAX_SIZE, sizeof(char));
offset += IPFIX_MESSAGE_HEAD_LEN; // skip ipfix_message_head
*(uint16_t *)(buff + offset) = htons(template_item->template_id); // data_flow_set_id
offset += sizeof(uint16_t); // skip data_flow_set_id
offset += sizeof(uint16_t); // skip data_flow_set_length
for (size_t i = 0; i < n_elements; i++)
{
struct ipfix_element *p_element = (struct ipfix_element *)utarray_eltptr(template_item->elements_array, i);
enum utable_value_type value_type = utable_get_value_type(table, p_element->name);
switch (value_type)
{
case utable_value_type_cstring:
case utable_value_type_blob:
{
char *value = NULL;
size_t sz_value = 0;
utable_get0_cstring_value(table, p_element->name, &value, &sz_value);
buff = ipfix_data_variable_serialize(buff, &offset, &buff_sz, value, sz_value);
break;
}
case utable_value_type_integer:
{
int64_t value = 0;
utable_get0_integer_value(table, p_element->name, &value);
buff = ipfix_data_interger_serialize(buff, &offset, &buff_sz, value, p_element->value_length);
break;
}
case utable_value_type_integer_array:
{
size_t n_array = 0;
int64_t *value_array = NULL;
utable_get0_integer_value_array(table, p_element->name, &value_array, &n_array);
yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
yyjson_mut_val *arr = yyjson_mut_arr_with_sint64(doc, value_array, n_array);
yyjson_mut_doc_set_root(doc, arr);
char *value_array_str = yyjson_mut_write(doc, 0, NULL);
if(value_array_str)
{
buff = ipfix_data_variable_serialize(buff, &offset, &buff_sz, value_array_str,
strlen(value_array_str));
yyjson_mut_doc_free(doc);
free(value_array_str);
}
break;
}
case utable_value_type_cstring_array:
{
size_t n_array = 0;
char **value_array = NULL;
size_t *value_sz = NULL;
utable_get0_cstring_value_array(table, p_element->name, &value_array, &value_sz, &n_array);
yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
yyjson_mut_val *arr = yyjson_mut_arr_with_strn(doc, (const char **)value_array, value_sz,n_array);
yyjson_mut_doc_set_root(doc, arr);
char *value_array_str = yyjson_mut_write(doc, 0, NULL);
if (value_array_str)
{
buff = ipfix_data_variable_serialize(buff, &offset, &buff_sz, value_array_str,
strlen(value_array_str));
yyjson_mut_doc_free(doc);
free(value_array_str);
}
break;
}
default: // utable_value_type_undefined, ipfix append 0
{
switch (p_element->type)
{
case IPFIX_UNSIGNED8:
case IPFIX_UNSIGNED16:
case IPFIX_UNSIGNED32:
case IPFIX_UNSIGNED64:
buff = ipfix_data_interger_serialize(buff, &offset, &buff_sz, 0, p_element->value_length);
break;
case IPFIX_VARIABLE_STRING:
buff = ipfix_data_variable_serialize(buff, &offset, &buff_sz, NULL, 0);
break;
default:
{
free(buff);
buff = NULL;
return -1;
}
}
break;
}
}
}
*(uint16_t *)(buff + IPFIX_MESSAGE_HEAD_LEN + sizeof(uint16_t)) = htons(offset - IPFIX_MESSAGE_HEAD_LEN); // data_flow_set_length
struct ipfix_message_head *p_message_head = (struct ipfix_message_head *)(buff);
p_message_head->version = htons(instance->version);
p_message_head->length = htons(offset);
p_message_head->exporttime = htonl(time(NULL)); // time_t is long int, but exporttime is int
p_message_head->ipfix_message_sequence = htonl(instance->worker_context_array[worker_id].sequence++);
p_message_head->domain_id = htonl(instance->worker_context_array[worker_id].source_id);
*blob = buff;
*blob_len = offset;
return 0;
}
const char *utable_ipfix_template_flow_get0(struct ipfix_exporter_schema *instance, uint16_t worker_id, size_t *blob_len)
{
if (instance == NULL || instance->worker_context_array == NULL || worker_id >= instance->n_worker)
{
return NULL;
}
struct ipfix_message_head *p_message_head = (struct ipfix_message_head *)(instance->worker_context_array[worker_id].template_blob);
p_message_head->exporttime = htonl(time(NULL)); // time_t is long int, but exporttime is int
p_message_head->ipfix_message_sequence = htonl(instance->worker_context_array[worker_id].sequence); // template sequence is not increase
*blob_len = instance->worker_context_array[worker_id].template_blob_length;
return instance->worker_context_array[worker_id].template_blob;
}
uint32_t utable_ipfix_template_flow_refresh_interval_s(struct ipfix_exporter_schema *ipfix_schema)
{
return ipfix_schema->templates_refresh_interval_s;
}