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>
19 vnet_flow_data_callback_t *flow_data_callback;
20 vnet_flow_rewrite_callback_t *rewrite_callback;
25 } vnet_flow_report_add_del_args_t;
29 flow_report_main_t *frm = &flow_report_main;
30 vnet_flow_report_add_del_args_t a;
36 /* Set up time reference pair */
37 mlm->vlib_time_0 = vlib_time_now (vm);
38 mlm->milisecond_time_0 = unix_time_now_nsec () * 1e-6;
42 memset (&a, 0, sizeof (a));
43 a.is_add = 1 /* to enable the report */;
44 a.domain_id = 1 /* pick a domain ID */;
45 a.src_port = UDP_DST_PORT_ipfix /* src port for reports */;
46 a.rewrite_callback = my_template_packet_rewrite_callback;
47 a.flow_data_callback = my_flow_data_callback;
49 /* Create the report */
50 rv = vnet_flow_report_add_del (frm, &a, &template_id);
54 /* Save the template-ID for later use */
55 mlm->template_id = template_id;
59 Several functions are worth describing in detail.
61 ### template packet rewrite callback function
63 This callback helps build ipfix template packets when required. We
64 should reduce the amount of cut-'n-paste coding, since only a fraction
65 of the code has anything to do with the specific ipfix template we're
70 my_template_packet_rewrite_callback (flow_report_main_t * frm,
72 ip4_address_t * collector_address,
73 ip4_address_t * src_address,
76 my_logging_main_t *mlm = &my_logging_main; /* typical */
79 ipfix_message_header_t *h;
80 ipfix_set_header_t *s;
81 ipfix_template_header_t *t;
82 ipfix_field_specifier_t *f;
83 ipfix_field_specifier_t *first_field;
85 ip4_ipfix_template_packet_t *tp;
87 flow_report_stream_t *stream;
89 stream = &frm->streams[fr->stream_index];
91 field_count = number_of_fields_to_export;
93 /* allocate rewrite space */
94 vec_validate_aligned (rewrite,
95 sizeof (ip4_ipfix_template_packet_t)
96 + field_count * sizeof (ipfix_field_specifier_t) - 1,
97 CLIB_CACHE_LINE_BYTES);
99 /* create the packet rewrite string */
100 tp = (ip4_ipfix_template_packet_t *) rewrite;
101 ip = (ip4_header_t *) & tp->ip4;
102 udp = (udp_header_t *) (ip + 1);
103 h = (ipfix_message_header_t *) (udp + 1);
104 s = (ipfix_set_header_t *) (h + 1);
105 t = (ipfix_template_header_t *) (s + 1);
106 first_field = f = (ipfix_field_specifier_t *) (t + 1);
108 ip->ip_version_and_header_length = 0x45;
110 ip->protocol = IP_PROTOCOL_UDP;
111 ip->src_address.as_u32 = src_address->as_u32;
112 ip->dst_address.as_u32 = collector_address->as_u32;
113 udp->src_port = clib_host_to_net_u16 (stream->src_port);
114 udp->dst_port = clib_host_to_net_u16 (collector_port);
115 udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip));
117 /* FIXUP LATER: message header export_time */
118 h->domain_id = clib_host_to_net_u32 (stream->domain_id);
121 * Add your favorite info elements to the template. See
122 * .../src/vnet/ipfix-export/ipfix_info_elements.h
124 * Highly advisable to make sure field count is correct!
127 f->e_id_length = ipfix_e_id_length (0, sourceIPv6Address, 16);
129 f->e_id_length = ipfix_e_id_length (0, postNATSourceIPv4Address, 4);
132 /* Back to the template packet... */
133 ip = (ip4_header_t *) & tp->ip4;
134 udp = (udp_header_t *) (ip + 1);
136 ASSERT (f - first_field);
137 /* Field count in this template */
138 t->id_count = ipfix_id_count (fr->template_id, f - first_field);
140 /* set length in octets */
142 ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
144 /* message length in octets */
145 h->version_length = version_length ((u8 *) f - (u8 *) h);
147 ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip);
148 ip->checksum = ip4_header_checksum (ip);
154 ### my_flow_data_callback
156 The ipfix flow export infrastructure calls this callback to flush the
157 current ipfix packet; to make sure that ipfix data is not retained for
158 an unreasonably long period of time.
160 We typically code it as shown below, to call an application-specific
161 function with (uninteresting arguments), and "do_flush = 1":
166 vlib_frame_t *my_flow_data_callback
167 (flow_report_main_t * frm,
170 u32 * to_next, u32 node_index)
173 my_buffer_flow_record (0, ... , 0, 1 /* do_flush */);
178 ### my_flow_data_header
180 This function creates the packet header for an ipfix data packet
185 my_flow_report_header (flow_report_main_t * frm,
186 vlib_buffer_t * b0, u32 * offset)
188 snat_ipfix_logging_main_t *mlm = &my_logging_main;
189 flow_report_stream_t *stream;
190 ip4_ipfix_template_packet_t *tp;
191 ipfix_message_header_t *h = 0;
192 ipfix_set_header_t *s = 0;
196 stream = &frm->streams[mlm->stream_index];
198 b0->current_data = 0;
199 b0->current_length = sizeof (*ip) + sizeof (*udp) + sizeof (*h) +
201 b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_FLOW_REPORT);
202 vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
203 vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index;
204 tp = vlib_buffer_get_current (b0);
205 ip = (ip4_header_t *) & tp->ip4;
206 udp = (udp_header_t *) (ip + 1);
207 h = (ipfix_message_header_t *) (udp + 1);
208 s = (ipfix_set_header_t *) (h + 1);
210 ip->ip_version_and_header_length = 0x45;
212 ip->protocol = IP_PROTOCOL_UDP;
213 ip->flags_and_fragment_offset = 0;
214 ip->src_address.as_u32 = frm->src_address.as_u32;
215 ip->dst_address.as_u32 = frm->ipfix_collector.as_u32;
216 udp->src_port = clib_host_to_net_u16 (stream->src_port);
217 udp->dst_port = clib_host_to_net_u16 (frm->collector_port);
220 h->export_time = clib_host_to_net_u32 ((u32)
221 (((f64) frm->unix_time_0) +
222 (vlib_time_now (frm->vlib_main) -
224 h->sequence_number = clib_host_to_net_u32 (stream->sequence_number++);
225 h->domain_id = clib_host_to_net_u32 (stream->domain_id);
227 *offset = (u32) (((u8 *) (s + 1)) - (u8 *) tp);
231 ### fixup and transmit a flow record
236 my_send_ipfix_pkt (flow_report_main_t * frm,
237 vlib_frame_t * f, vlib_buffer_t * b0, u16 template_id)
239 ip4_ipfix_template_packet_t *tp;
240 ipfix_message_header_t *h = 0;
241 ipfix_set_header_t *s = 0;
244 vlib_main_t *vm = frm->vlib_main;
246 tp = vlib_buffer_get_current (b0);
247 ip = (ip4_header_t *) & tp->ip4;
248 udp = (udp_header_t *) (ip + 1);
249 h = (ipfix_message_header_t *) (udp + 1);
250 s = (ipfix_set_header_t *) (h + 1);
252 s->set_id_length = ipfix_set_id_length (template_id,
254 (sizeof (*ip) + sizeof (*udp) +
256 h->version_length = version_length (b0->current_length -
257 (sizeof (*ip) + sizeof (*udp)));
259 ip->length = clib_host_to_net_u16 (b0->current_length);
260 ip->checksum = ip4_header_checksum (ip);
261 udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
263 if (frm->udp_checksum)
265 udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
266 if (udp->checksum == 0)
267 udp->checksum = 0xffff;
270 ASSERT (ip->checksum == ip4_header_checksum (ip));
272 vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
276 ### my_buffer_flow_record
278 This is the key routine which paints individual flow records into
279 an ipfix packet under construction. It's pretty straightforward
280 (albeit stateful) vpp data-plane code.
285 my_buffer_flow_record (u32 datum0, u32 datum1, ..., int do_flush)
287 my_logging_main_t *mlm = &my_logging_main;
288 flow_report_main_t *frm = &flow_report_main;
290 vlib_buffer_t *b0 = 0;
293 vlib_main_t *vm = frm->vlib_main;
295 vlib_buffer_free_list_t *fl;
296 my_flow_record_t my_flow_record;
301 now = (u64) ((vlib_time_now (vm) - silm->vlib_time_0) * 1e3);
302 now += mlm->milisecond_time_0;
305 * (maybe) set up a packed structure from datum0...datumN
306 * Otherwise, paint directly into the buffer below...
308 my_flow_record.xxx = datum0;
309 my_flow_record.yyy = datum1;
312 b0 = mlm->my_data_buffer;
314 if (PREDICT_FALSE (b0 == 0))
319 if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
321 clib_warning ("can't allocate ipfix data buffer");
325 b0 = mlm->my_data_buffer = vlib_get_buffer (vm, bi0);
327 vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX);
328 vlib_buffer_init_for_free_list (b0, fl);
329 VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
334 bi0 = vlib_get_buffer_index (vm, b0);
335 offset = mlm->my_next_record_offset;
338 f = mlm->my_ipfix_frame;
339 if (PREDICT_FALSE (f == 0))
342 f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
343 mlm->my_ipfix_frame = f;
344 to_next = vlib_frame_vector_args (f);
349 if (PREDICT_FALSE (offset == 0))
350 my_flow_report_header (frm, b0, &offset);
352 if (PREDICT_TRUE (do_flush == 0))
354 /* paint time stamp into buffer */
355 clib_memcpy (b0->data + offset, &time_stamp, sizeof (time_stamp));
356 offset += sizeof (time_stamp);
358 /* Paint the new ipfix data record into the buffer */
359 clib_memcpy (b0->data + offset, &my_flow_record,
360 sizeof (my_flow_record));
361 offset += sizeof (my_flow_record);
362 b0->current_length += sizeof(my_flow_record);
366 (do_flush || (offset + sizeof (my_flow_record)) > frm->path_mtu))
368 my_send_ipfix_pkt (frm, f, b0, mlm->template_id);
369 mlm->my_ipfix_frame = 0;
370 mlm->my_data_buffer = 0;
373 mlm->next_record_offset = offset;