FD.io VPP  v21.10.1-2-g0a485f517
Vector Packet Processing
IPFIX support

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.

/* Defined in flow_report.h, of interest when constructing reports */
/* ipfix field definitions for a particular report */
typedef struct
{
u32 info_element;
/* Report add/del argument structure */
typedef struct
{
/* Callback to flush current ipfix packet / frame */
vnet_flow_data_callback_t *flow_data_callback;
/* Callback to build the template packet rewrite string */
vnet_flow_rewrite_callback_t *rewrite_callback;
/* List of ipfix elements in the report */
ipfix_report_element_t *report_elements;
u32 n_report_elements;
/* Kept in flow report, used e.g. by flow classifier */
opaque_t opaque;
/* Add / delete a report */
int is_add;
/* Ipfix "domain-ID", see RFC, set as desired */
u32 domain_id;
/* ipfix packet source port, often set to UDP_DST_PORT_ipfix */
/* Set by ipfix infra, needed to send data packets */
u32 *stream_indexp;
/* Private header file contents */
/* Report ipfix element definition */
#define foreach_simple_report_ipfix_element \
_(sourceIPv4Address, 4) \
_(destinationIPv4Address, 4) \
_(sourceTransportPort, 2) \
_(destinationTransportPort, 2) \
_(protocolIdentifier, 1) \
_(flowStartMicroseconds, 8) \
_(flowEndMicroseconds, 8)
static ipfix_report_element_t simple_report_elements[] = {
#define _(a,b) {a,b},
foreach_simple_report_ipfix_element
#undef _
};
typedef struct
{
/** Buffers and frames, per thread */
vlib_buffer_t **buffers_by_thread;
vlib_frame_t **frames_by_thread;
u32 *next_record_offset_by_thread;
/** Template ID's */
u16 *template_ids;
/** Time reference pair */
u64 usec_time_0;
f64 vlib_time_0;
/** Stream index */
u32 stream_index;
/* Convenience */
} my_logging_main_t;
extern my_logging_main_t my_logging_main;
...
/* Recitations */
flow_report_main_t *frm = &flow_report_main;
my_logging_main_t *mlm = &my_logging_main;
int rv;
u16 template_id;
...
/* Init function: set up time reference pair */
mlm->vlib_time_0 = vlib_time_now (vm);
mlm->milisecond_time_0 = unix_time_now_nsec () * 1e-6;
...
/* Create a report */
memset (&a, 0, sizeof (a));
a.is_add = 1 /* to enable the report */;
a.domain_id = 1 /* pick a domain ID */;
a.src_port = UDP_DST_PORT_ipfix /* src port for reports */;
/* Use the generic template packet rewrite string generator */
/* Supply a list of ipfix report elements */
a.report_elements = simple_report_elements;
a.n_report_elements = ARRAY_LEN (simple_report_elements);
/* Pointer to the ipfix stream index, set by the report infra */
a.stream_indexp = &mlm->stream_index;
a.flow_data_callback = my_flow_data_callback;
/* Create the report */
rv = vnet_flow_report_add_del (frm, &a, &template_id);
if (rv)
oops...
/* Save the template-ID for later use */
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":

vlib_frame_t *my_flow_data_callback
u32 * to_next, u32 node_index)
{
my_buffer_flow_record (0, ... , 0, 1 /* do_flush */);
return f;
}

my_flow_data_header

This function creates the packet header for an ipfix data packet

static inline void
my_flow_report_header (flow_report_main_t * frm,
{
my_logging_main_t *mlm = &my_logging_main;
stream = &frm->streams[mlm->stream_index];
b0->current_data = 0;
b0->current_length = sizeof (*ip) + sizeof (*udp) + sizeof (*h) +
sizeof (*s);
b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_FLOW_REPORT);
vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index;
ip = (ip4_header_t *) & tp->ip4;
udp = (udp_header_t *) (ip + 1);
h = (ipfix_message_header_t *) (udp + 1);
s = (ipfix_set_header_t *) (h + 1);
ip->ip_version_and_header_length = 0x45;
ip->ttl = 254;
ip->protocol = IP_PROTOCOL_UDP;
ip->flags_and_fragment_offset = 0;
ip->src_address.as_u32 = frm->src_address.as_u32;
ip->dst_address.as_u32 = frm->ipfix_collector.as_u32;
udp->src_port = clib_host_to_net_u16 (stream->src_port);
udp->dst_port = clib_host_to_net_u16 (frm->collector_port);
udp->checksum = 0;
h->export_time = clib_host_to_net_u32 ((u32)
(((f64) frm->unix_time_0) +
frm->vlib_time_0)));
h->sequence_number = clib_host_to_net_u32 (stream->sequence_number++);
h->domain_id = clib_host_to_net_u32 (stream->domain_id);
*offset = (u32) (((u8 *) (s + 1)) - (u8 *) tp);
}

fixup and transmit a flow record

static inline void
my_send_ipfix_pkt (flow_report_main_t * frm,
vlib_frame_t * f, vlib_buffer_t * b0, u16 template_id)
{
ip = (ip4_header_t *) & tp->ip4;
udp = (udp_header_t *) (ip + 1);
h = (ipfix_message_header_t *) (udp + 1);
s = (ipfix_set_header_t *) (h + 1);
(sizeof (*ip) + sizeof (*udp) +
sizeof (*h)));
h->version_length = version_length (b0->current_length -
(sizeof (*ip) + sizeof (*udp)));
ip->length = clib_host_to_net_u16 (b0->current_length);
ip->checksum = ip4_header_checksum (ip);
udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
if (frm->udp_checksum)
{
if (udp->checksum == 0)
udp->checksum = 0xffff;
}
}

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,
{
vlib_main_t *vm = vlib_mains[thread_index];
my_logging_main_t *mlm = &jvp_ipfix_main;
vlib_buffer_t *b0 = 0;
u32 bi0 = ~0;
b0 = mlm->buffers_by_thread[thread_index];
if (PREDICT_FALSE (b0 == 0))
{
if (do_flush)
return;
if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
{
clib_warning ("can't allocate ipfix data buffer");
return;
}
b0 = vlib_get_buffer (vm, bi0);
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];
if (PREDICT_FALSE (f == 0))
{
u32 *to_next;
mlm->frames_by_thread[thread_index] = f;
to_next[0] = bi0;
f->n_vectors = 1;
mlm->frames_by_thread[thread_index] = f;
}
if (PREDICT_FALSE (offset == 0))
my_flow_report_header (frm, b0, &offset);
if (PREDICT_TRUE (do_flush == 0))
{
/* Paint the new ipfix data record into the buffer */
clib_memcpy (b0->data + offset, rp, sizeof (*rp));
offset += sizeof (*rp);
b0->current_length += sizeof (*rp);
}
if (PREDICT_FALSE (do_flush || (offset + sizeof (*rp)) > frm->path_mtu))
{
/* Nothing to send? */
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);
}
ip4_lookup_node
vlib_node_registration_t ip4_lookup_node
(constructor) VLIB_REGISTER_NODE (ip4_lookup_node)
Definition: ip4_forward.c:105
vlib_frame_t::n_vectors
u16 n_vectors
Definition: node.h:387
flow_report_stream_t::sequence_number
u32 sequence_number
Definition: flow_report.h:79
udp_header_t::src_port
u16 src_port
Definition: udp_packet.h:48
udp_header_t::length
u16 length
Definition: udp_packet.h:51
flow_report_main
flow_report_main_t flow_report_main
Definition: flow_report.c:22
thread_index
u32 thread_index
Definition: nat44_ei_hairpinning.c:495
flow_report_main::udp_checksum
u8 udp_checksum
Definition: flow_report.h:128
flow_report_main::vlib_main
vlib_main_t * vlib_main
Definition: flow_report.h:135
clib_memcpy
#define clib_memcpy(d, s, n)
Definition: string.h:197
vlib_get_buffer
static vlib_buffer_t * vlib_get_buffer(vlib_main_t *vm, u32 buffer_index)
Translate buffer index into buffer pointer.
Definition: buffer_funcs.h:111
f
vlib_frame_t * f
Definition: interface_output.c:1098
ipfix_set_header_t
Definition: ipfix_packet.h:115
ip4_header_checksum_is_valid
static uword ip4_header_checksum_is_valid(ip4_header_t *i)
Definition: ip4_packet.h:396
ip4_address_t::as_u32
u32 as_u32
Definition: ip4_packet.h:57
flow_report_stream_t::domain_id
u32 domain_id
Definition: flow_report.h:78
flow_report
Definition: flow_report.h:85
u16
unsigned short u16
Definition: types.h:57
vm
vlib_main_t * vm
X-connect all packets from the HOST to the PHY.
Definition: nat44_ei.c:3047
VLIB_RX
@ VLIB_RX
Definition: defs.h:46
node_index
node node_index
Definition: interface_output.c:440
vnet_flow_rewrite_callback_t
u8 *() vnet_flow_rewrite_callback_t(struct flow_report_main *, struct flow_report *, ip4_address_t *, ip4_address_t *, u16, ipfix_report_element_t *elts, u32 n_elts, u32 *stream_index)
Definition: flow_report.h:56
flow_report_main::fib_index
u32 fib_index
Definition: flow_report.h:119
vlib_frame_t
Definition: node.h:372
vlib_get_frame_to_node
vlib_frame_t * vlib_get_frame_to_node(vlib_main_t *vm, u32 to_node_index)
Definition: main.c:184
udp_header_t
Definition: udp_packet.h:45
ip4_header_t
Definition: ip4_packet.h:87
h
h
Definition: flowhash_template.h:372
ip4_ipfix_template_packet_t
Definition: flow_report.h:41
vlib_buffer_t::current_data
i16 current_data
signed offset in data[], pre_data[] that we are currently processing.
Definition: buffer.h:119
vlib_put_frame_to_node
void vlib_put_frame_to_node(vlib_main_t *vm, u32 to_node_index, vlib_frame_t *f)
Definition: main.c:218
vlib_buffer_alloc
static __clib_warn_unused_result u32 vlib_buffer_alloc(vlib_main_t *vm, u32 *buffers, u32 n_buffers)
Allocate buffers into supplied array.
Definition: buffer_funcs.h:702
flow_report_stream_t::src_port
u16 src_port
Definition: flow_report.h:80
vnet_buffer
#define vnet_buffer(b)
Definition: buffer.h:441
offset
struct clib_bihash_value offset
template key/value backing page structure
vnet_flow_rewrite_generic_callback
u8 * vnet_flow_rewrite_generic_callback(flow_report_main_t *frm, flow_report_t *fr, ip4_address_t *collector_address, ip4_address_t *src_address, u16 collector_port, ipfix_report_element_t *report_elts, u32 n_elts, u32 *stream_indexp)
Definition: flow_report.c:166
PREDICT_FALSE
#define PREDICT_FALSE(x)
Definition: clib.h:124
vlib_get_thread_index
static_always_inline uword vlib_get_thread_index(void)
Definition: threads.h:187
ARRAY_LEN
#define ARRAY_LEN(x)
Definition: clib.h:70
vlib_get_buffer_index
static u32 vlib_get_buffer_index(vlib_main_t *vm, void *p)
Translate buffer pointer into buffer index.
Definition: buffer_funcs.h:324
vlib_frame_vector_args
static void * vlib_frame_vector_args(vlib_frame_t *f)
Get pointer to frame vector data.
Definition: node_funcs.h:301
ip4_tcp_udp_compute_checksum
u16 ip4_tcp_udp_compute_checksum(vlib_main_t *vm, vlib_buffer_t *p0, ip4_header_t *ip0)
Definition: pnat_test_stubs.h:59
flow_report_main::ipfix_collector
ip4_address_t ipfix_collector
Definition: flow_report.h:116
vnet_flow_data_callback_t
vlib_frame_t *() vnet_flow_data_callback_t(struct flow_report_main *, struct flow_report *, vlib_frame_t *, u32 *, u32)
Definition: flow_report.h:51
unix_time_now_nsec
static u64 unix_time_now_nsec(void)
Definition: time.h:270
ipfix_report_element_t
Definition: flow_report.h:34
opaque_t
Definition: flow_report.h:70
src_port
vl_api_ip_port_and_mask_t src_port
Definition: flow_types.api:91
f64
double f64
Definition: types.h:142
flow_report_main
Definition: flow_report.h:110
version_length
static u32 version_length(u16 length)
Definition: ipfix_packet.h:33
flow_report_main::streams
flow_report_stream_t * streams
Definition: flow_report.h:113
ipfix_message_header_t
Definition: ipfix_packet.h:24
vlib_buffer_t::current_length
u16 current_length
Nbytes between current data and the end of this buffer.
Definition: buffer.h:122
flow_report.h
flow_report_main::collector_port
u16 collector_port
Definition: flow_report.h:117
flow_report_main::path_mtu
u32 path_mtu
Definition: flow_report.h:122
udp_header_t::checksum
u16 checksum
Definition: udp_packet.h:55
vnet_main_t
Definition: vnet.h:76
size
u32 size
Definition: vhost_user.h:125
flow_report_main::src_address
ip4_address_t src_address
Definition: flow_report.h:118
clib_bihash_value
template key/value backing page structure
Definition: bihash_doc.h:44
ip4_ipfix_template_packet_t::ip4
ip4_header_t ip4
Definition: flow_report.h:43
u64
unsigned long u64
Definition: types.h:89
ASSERT
#define ASSERT(truth)
Definition: error_bootstrap.h:69
u32
unsigned int u32
Definition: types.h:88
udp_header_t::dst_port
u16 dst_port
Definition: udp_packet.h:48
flow_report_stream_t
Definition: flow_report.h:76
vnet_flow_report_add_del_args_t
Definition: flow_report.h:147
flow_report_main::unix_time_0
u32 unix_time_0
Definition: flow_report.h:131
vnet_main
vnet_main_t vnet_main
Definition: misc.c:43
vlib_main_t
Definition: main.h:102
u8
unsigned char u8
Definition: types.h:56
a
a
Definition: bitmap.h:525
vlib_buffer_get_current
static void * vlib_buffer_get_current(vlib_buffer_t *b)
Get pointer to current data to process.
Definition: buffer.h:257
ip
vl_api_address_t ip
Definition: l2.api:558
ip4_header_checksum
static u16 ip4_header_checksum(ip4_header_t *i)
Definition: ip4_packet.h:314
vlib_buffer_t::data
u8 data[]
Packet data.
Definition: buffer.h:204
ipfix_set_id_length
static u32 ipfix_set_id_length(u16 set_id, u16 length)
Definition: ipfix_packet.h:121
clib_warning
#define clib_warning(format, args...)
Definition: error.h:59
flow_report_main::vlib_time_0
f64 vlib_time_0
Definition: flow_report.h:132
rv
int __clib_unused rv
Definition: application.c:491
vnet_flow_report_add_del
int vnet_flow_report_add_del(flow_report_main_t *frm, vnet_flow_report_add_del_args_t *a, u16 *template_id)
Definition: flow_report.c:344
vlib_time_now
static f64 vlib_time_now(vlib_main_t *vm)
Definition: main.h:327
vlib_main
int vlib_main(vlib_main_t *volatile vm, unformat_input_t *input)
Definition: main.c:1914
PREDICT_TRUE
#define PREDICT_TRUE(x)
Definition: clib.h:125
VLIB_TX
@ VLIB_TX
Definition: defs.h:47
ipfix_set_header_t::set_id_length
u32 set_id_length
Definition: ipfix_packet.h:117
vlib_buffer_t::flags
u32 flags
buffer flags: VLIB_BUFFER_FREE_LIST_INDEX_MASK: bits used to store free list index,...
Definition: buffer.h:133
vlib_buffer_t
VLIB buffer representation.
Definition: buffer.h:111