VPP includes a high-performance IPFIX record exporter. This note explains how to use the internal APIs to export IPFIX data, and how to configure and send the required IPFIX templates.
As you'll see, a bit of typing is required.
First: create an ipfix "report"
Include the flow report header file, fill out a vnet_flow_report_add_del_args_t structure, and call vnet_flow_report_add_del.
typedef struct
{
typedef struct
{
int is_add;
#define foreach_simple_report_ipfix_element \
_(sourceIPv4Address, 4) \
_(destinationIPv4Address, 4) \
_(sourceTransportPort, 2) \
_(destinationTransportPort, 2) \
_(protocolIdentifier, 1) \
_(flowStartMicroseconds, 8) \
_(flowEndMicroseconds, 8)
#define _(a,b) {a,b},
foreach_simple_report_ipfix_element
#undef _
};
typedef struct
{
u32 *next_record_offset_by_thread;
} my_logging_main_t;
extern my_logging_main_t my_logging_main;
...
my_logging_main_t *mlm = &my_logging_main;
int rv;
...
...
memset (&a, 0, sizeof (a));
if (rv)
oops...
mlm->template_id = template_id;
Several things are worth describing in more detail.
vnet_flow_rewrite_generic_callback programming
This generic callback helps build ipfix template packets. When registering an ipfix report, pass an (array, count) of ipfix elements as shown above.
my_flow_data_callback
The ipfix flow export infrastructure calls this callback to flush the current ipfix packet; to make sure that ipfix data is not retained for an unreasonably long period of time.
We typically code it as shown below, to call an application-specific function with (uninteresting arguments), and "do_flush = 1":
u32 * to_next,
u32 node_index)
{
my_buffer_flow_record (0, ... , 0, 1 );
return f;
}
my_flow_data_header
This function creates the packet header for an ipfix data packet
static inline void
{
my_logging_main_t *mlm = &my_logging_main;
stream = &frm->
streams[mlm->stream_index];
sizeof (*s);
b0->
flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_FLOW_REPORT);
*offset = (
u32) (((
u8 *) (s + 1)) - (
u8 *) tp);
}
fixup and transmit a flow record
static inline void
{
(sizeof (*ip) + sizeof (*udp) +
sizeof (*h)));
(sizeof (*ip) + sizeof (*udp)));
{
}
}
my_buffer_flow_record
This is the key routine which paints individual flow records into an ipfix packet under construction. It's pretty straightforward (albeit stateful) vpp data-plane code. The code shown below is thread-safe by construction.
static inline void
my_buffer_flow_record_internal (my_flow_record_t * rp, int do_flush,
{
my_logging_main_t *mlm = &jvp_ipfix_main;
b0 = mlm->buffers_by_thread[thread_index];
{
if (do_flush)
return;
{
return;
}
offset = 0;
mlm->buffers_by_thread[thread_index] = b0;
}
else
{
offset = mlm->next_record_offset_by_thread[thread_index];
}
f = mlm->frames_by_thread[thread_index];
{
mlm->frames_by_thread[thread_index] = f;
to_next[0] = bi0;
mlm->frames_by_thread[thread_index] = f;
}
my_flow_report_header (frm, b0, &offset);
{
offset += sizeof (*rp);
}
{
if (offset == 0)
return;
send_ipfix_pkt (frm, f, b0, mlm->template_ids[0]);
mlm->buffers_by_thread[thread_index] = 0;
mlm->frames_by_thread[thread_index] = 0;
offset = 0;
}
mlm->next_record_offset_by_thread[thread_index] =
offset;
}
static void
my_buffer_flow_record (my_flow_record_t * rp, int do_flush)
{
my_buffer_flow_record_internal (rp, do_flush, thread_index);
}