1 # IPFIX support {#ipfix}
3 VPP includes a high-performance IPFIX record exporter. This note
4 explains how to use the internal APIs to export IPFIX data, and how to
5 configure and send the required IPFIX templates.
7 As you'll see, a bit of typing is required.
9 ## First: create an ipfix "report"
11 Include the flow report header file, fill out a @ref
12 vnet_flow_report_add_del_args_t structure, and call vnet_flow_report_add_del.
15 #include <vnet/ipfix-export/flow_report.h>
16 /* Defined in flow_report.h, of interest when constructing reports */
18 /* ipfix field definitions for a particular report */
23 } ipfix_report_element_t;
25 /* Report add/del argument structure */
28 /* Callback to flush current ipfix packet / frame */
29 vnet_flow_data_callback_t *flow_data_callback;
31 /* Callback to build the template packet rewrite string */
32 vnet_flow_rewrite_callback_t *rewrite_callback;
34 /* List of ipfix elements in the report */
35 ipfix_report_element_t *report_elements;
36 u32 n_report_elements;
37 /* Kept in flow report, used e.g. by flow classifier */
39 /* Add / delete a report */
41 /* Ipfix "domain-ID", see RFC, set as desired */
43 /* ipfix packet source port, often set to UDP_DST_PORT_ipfix */
45 /* Set by ipfix infra, needed to send data packets */
47 } vnet_flow_report_add_del_args_t;
49 /* Private header file contents */
51 /* Report ipfix element definition */
52 #define foreach_simple_report_ipfix_element \
53 _(sourceIPv4Address, 4) \
54 _(destinationIPv4Address, 4) \
55 _(sourceTransportPort, 2) \
56 _(destinationTransportPort, 2) \
57 _(protocolIdentifier, 1) \
58 _(flowStartMicroseconds, 8) \
59 _(flowEndMicroseconds, 8)
61 static ipfix_report_element_t simple_report_elements[] = {
63 foreach_simple_report_ipfix_element
69 /** Buffers and frames, per thread */
70 vlib_buffer_t **buffers_by_thread;
71 vlib_frame_t **frames_by_thread;
72 u32 *next_record_offset_by_thread;
77 /** Time reference pair */
85 flow_report_main_t *flow_report_main;
86 vlib_main_t *vlib_main;
87 vnet_main_t *vnet_main;
90 extern my_logging_main_t my_logging_main;
95 flow_report_main_t *frm = &flow_report_main;
96 my_logging_main_t *mlm = &my_logging_main;
97 vnet_flow_report_add_del_args_t a;
103 /* Init function: set up time reference pair */
104 mlm->vlib_time_0 = vlib_time_now (vm);
105 mlm->milisecond_time_0 = unix_time_now_nsec () * 1e-6;
109 /* Create a report */
110 memset (&a, 0, sizeof (a));
111 a.is_add = 1 /* to enable the report */;
112 a.domain_id = 1 /* pick a domain ID */;
113 a.src_port = UDP_DST_PORT_ipfix /* src port for reports */;
115 /* Use the generic template packet rewrite string generator */
116 a.rewrite_callback = vnet_flow_rewrite_generic_callback;
118 /* Supply a list of ipfix report elements */
119 a.report_elements = simple_report_elements;
120 a.n_report_elements = ARRAY_LEN (simple_report_elements);
122 /* Pointer to the ipfix stream index, set by the report infra */
123 a.stream_indexp = &mlm->stream_index;
124 a.flow_data_callback = my_flow_data_callback;
126 /* Create the report */
127 rv = vnet_flow_report_add_del (frm, &a, &template_id);
131 /* Save the template-ID for later use */
132 mlm->template_id = template_id;
136 Several things are worth describing in more detail.
138 ### vnet_flow_rewrite_generic_callback programming
140 This generic callback helps build ipfix template packets. When
141 registering an ipfix report, pass an (array, count)
142 of ipfix elements as shown above.
144 ### my_flow_data_callback
146 The ipfix flow export infrastructure calls this callback to flush the
147 current ipfix packet; to make sure that ipfix data is not retained for
148 an unreasonably long period of time.
150 We typically code it as shown below, to call an application-specific
151 function with (uninteresting arguments), and "do_flush = 1":
156 vlib_frame_t *my_flow_data_callback
157 (flow_report_main_t * frm,
160 u32 * to_next, u32 node_index)
163 my_buffer_flow_record (0, ... , 0, 1 /* do_flush */);
168 ### my_flow_data_header
170 This function creates the packet header for an ipfix data packet
175 my_flow_report_header (flow_report_main_t * frm,
176 vlib_buffer_t * b0, u32 * offset)
178 my_logging_main_t *mlm = &my_logging_main;
179 flow_report_stream_t *stream;
180 ip4_ipfix_template_packet_t *tp;
181 ipfix_message_header_t *h = 0;
184 ipfix_set_header_t *s = 0;
188 stream = &frm->streams[mlm->stream_index];
190 b0->current_data = 0;
191 b0->current_length = sizeof (*ip) + sizeof (*udp) + sizeof (*h) +
193 b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_FLOW_REPORT);
194 vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
195 vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index;
196 tp = vlib_buffer_get_current (b0);
197 ip = (ip4_header_t *) & tp->ip4;
198 udp = (udp_header_t *) (ip + 1);
199 h = (ipfix_message_header_t *) (udp + 1);
200 s = (ipfix_set_header_t *) (h + 1);
202 ip->ip_version_and_header_length = 0x45;
204 ip->protocol = IP_PROTOCOL_UDP;
205 ip->flags_and_fragment_offset = 0;
206 ip->src_address.as_u32 = frm->src_address.as_u32;
207 ip->dst_address.as_u32 = frm->ipfix_collector.as_u32;
208 udp->src_port = clib_host_to_net_u16 (stream->src_port);
209 udp->dst_port = clib_host_to_net_u16 (frm->collector_port);
212 h->export_time = clib_host_to_net_u32 ((u32)
213 (((f64) frm->unix_time_0) +
214 (vlib_time_now (frm->vlib_main) -
216 h->sequence_number = clib_host_to_net_u32 (stream->sequence_number++);
217 h->domain_id = clib_host_to_net_u32 (stream->domain_id);
219 *offset = (u32) (((u8 *) (s + 1)) - (u8 *) tp);
223 ### fixup and transmit a flow record
228 my_send_ipfix_pkt (flow_report_main_t * frm,
229 vlib_frame_t * f, vlib_buffer_t * b0, u16 template_id)
231 ip4_ipfix_template_packet_t *tp;
232 ipfix_message_header_t *h = 0;
233 ipfix_set_header_t *s = 0;
236 vlib_main_t *vm = frm->vlib_main;
238 tp = vlib_buffer_get_current (b0);
239 ip = (ip4_header_t *) & tp->ip4;
240 udp = (udp_header_t *) (ip + 1);
241 h = (ipfix_message_header_t *) (udp + 1);
242 s = (ipfix_set_header_t *) (h + 1);
244 s->set_id_length = ipfix_set_id_length (template_id,
246 (sizeof (*ip) + sizeof (*udp) +
248 h->version_length = version_length (b0->current_length -
249 (sizeof (*ip) + sizeof (*udp)));
251 ip->length = clib_host_to_net_u16 (b0->current_length);
252 ip->checksum = ip4_header_checksum (ip);
253 udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
255 if (frm->udp_checksum)
257 udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
258 if (udp->checksum == 0)
259 udp->checksum = 0xffff;
262 ASSERT (ip->checksum == ip4_header_checksum (ip));
264 vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
268 ### my_buffer_flow_record
270 This is the key routine which paints individual flow records into
271 an ipfix packet under construction. It's pretty straightforward
272 (albeit stateful) vpp data-plane code. The code shown below is
273 thread-safe by construction.
277 my_buffer_flow_record_internal (my_flow_record_t * rp, int do_flush,
280 vlib_main_t *vm = vlib_mains[thread_index];
281 my_logging_main_t *mlm = &jvp_ipfix_main;
282 flow_report_main_t *frm = &flow_report_main;
284 vlib_buffer_t *b0 = 0;
287 vlib_buffer_free_list_t *fl;
289 b0 = mlm->buffers_by_thread[thread_index];
291 if (PREDICT_FALSE (b0 == 0))
296 if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
298 clib_warning ("can't allocate ipfix data buffer");
302 b0 = vlib_get_buffer (vm, bi0);
304 vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX);
305 vlib_buffer_init_for_free_list (b0, fl);
306 VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
308 mlm->buffers_by_thread[thread_index] = b0;
312 bi0 = vlib_get_buffer_index (vm, b0);
313 offset = mlm->next_record_offset_by_thread[thread_index];
316 f = mlm->frames_by_thread[thread_index];
317 if (PREDICT_FALSE (f == 0))
320 f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
321 mlm->frames_by_thread[thread_index] = f;
322 to_next = vlib_frame_vector_args (f);
325 mlm->frames_by_thread[thread_index] = f;
328 if (PREDICT_FALSE (offset == 0))
329 my_flow_report_header (frm, b0, &offset);
331 if (PREDICT_TRUE (do_flush == 0))
333 /* Paint the new ipfix data record into the buffer */
334 clib_memcpy (b0->data + offset, rp, sizeof (*rp));
335 offset += sizeof (*rp);
336 b0->current_length += sizeof (*rp);
339 if (PREDICT_FALSE (do_flush || (offset + sizeof (*rp)) > frm->path_mtu))
341 /* Nothing to send? */
345 send_ipfix_pkt (frm, f, b0, mlm->template_ids[0]);
346 mlm->buffers_by_thread[thread_index] = 0;
347 mlm->frames_by_thread[thread_index] = 0;
350 mlm->next_record_offset_by_thread[thread_index] = offset;
354 my_buffer_flow_record (my_flow_record_t * rp, int do_flush)
356 u32 thread_index = vlib_get_thread_index();
357 my_buffer_flow_record_internal (rp, do_flush, thread_index);