octeon: add clear counters for port and queues
[vpp.git] / src / vnet / ipfix-export / ipfix_doc.rst
1 .. _ipfix_doc:
2
3 IPFIX support
4 =============
5
6 VPP includes a high-performance IPFIX record exporter. This note
7 explains how to use the internal APIs to export IPFIX data, and how to
8 configure and send the required IPFIX templates.
9
10 As you’ll see, a bit of typing is required.
11
12 First: create an ipfix “report”
13 -------------------------------
14
15 Include the flow report header file, fill out a @ref
16 vnet_flow_report_add_del_args_t structure, and call
17 vnet_flow_report_add_del.
18
19 .. code:: c
20
21       #include <vnet/ipfix-export/flow_report.h>
22       /* Defined in flow_report.h, of interest when constructing reports */
23
24       /* ipfix field definitions for a particular report */
25       typedef struct
26       {
27         u32 info_element;
28         u32 size;
29       } ipfix_report_element_t;
30
31       /* Report add/del argument structure */
32       typedef struct
33       {
34         /* Callback to flush current ipfix packet / frame */
35         vnet_flow_data_callback_t *flow_data_callback;
36
37         /* Callback to build the template packet rewrite string */
38         vnet_flow_rewrite_callback_t *rewrite_callback;
39
40         /* List of ipfix elements in the report */
41         ipfix_report_element_t *report_elements;
42         u32 n_report_elements;
43         /* Kept in flow report, used e.g. by flow classifier */
44         opaque_t opaque;
45         /* Add / delete a report */
46         int is_add;
47         /* Ipfix "domain-ID", see RFC, set as desired */
48         u32 domain_id;
49         /* ipfix packet source port, often set to UDP_DST_PORT_ipfix */
50         u16 src_port;
51         /* Set by ipfix infra, needed to send data packets */
52         u32 *stream_indexp;
53       } vnet_flow_report_add_del_args_t;
54
55       /* Private header file contents */
56
57       /* Report ipfix element definition */
58       #define foreach_simple_report_ipfix_element     \
59       _(sourceIPv4Address, 4)                         \
60       _(destinationIPv4Address, 4)                    \
61       _(sourceTransportPort, 2)                       \
62       _(destinationTransportPort, 2)                  \
63       _(protocolIdentifier, 1)                        \
64       _(flowStartMicroseconds, 8)                     \
65       _(flowEndMicroseconds, 8)
66
67       static ipfix_report_element_t simple_report_elements[] = {
68       #define _(a,b) {a,b},
69         foreach_simple_report_ipfix_element
70       #undef _
71       };
72
73       typedef struct
74       {
75         /** Buffers and frames, per thread */
76         vlib_buffer_t **buffers_by_thread;
77         vlib_frame_t **frames_by_thread;
78         u32 *next_record_offset_by_thread;
79
80         /** Template ID's */
81         u16 *template_ids;
82
83         /** Time reference pair */
84         u64 usec_time_0;
85         f64 vlib_time_0;
86
87         /** Stream index */
88         u32 stream_index;
89
90         /* Convenience */
91         flow_report_main_t *flow_report_main;
92         vlib_main_t *vlib_main;
93         vnet_main_t *vnet_main;
94       } my_logging_main_t;
95
96       extern my_logging_main_t my_logging_main;
97
98       ...
99
100       /* Recitations */
101       flow_report_main_t *frm = &flow_report_main;
102       my_logging_main_t *mlm = &my_logging_main;
103       vnet_flow_report_add_del_args_t a;
104       int rv;
105       u16 template_id;
106
107       ...
108
109       /* Init function: set up time reference pair */
110       mlm->vlib_time_0 = vlib_time_now (vm);
111       mlm->milisecond_time_0 = unix_time_now_nsec () * 1e-6;
112
113       ...
114
115       /* Create a report */
116       memset (&a, 0, sizeof (a));
117       a.is_add = 1 /* to enable the report */;
118       a.domain_id = 1 /* pick a domain ID */;
119       a.src_port = UDP_DST_PORT_ipfix /* src port for reports */;
120
121       /* Use the generic template packet rewrite string generator */
122       a.rewrite_callback = vnet_flow_rewrite_generic_callback;
123
124       /* Supply a list of ipfix report elements */
125       a.report_elements = simple_report_elements;
126       a.n_report_elements = ARRAY_LEN (simple_report_elements);
127
128       /* Pointer to the ipfix stream index, set by the report infra */
129       a.stream_indexp = &mlm->stream_index;
130       a.flow_data_callback = my_flow_data_callback;
131
132       /* Create the report */
133       rv = vnet_flow_report_add_del (frm, &a, &template_id);
134       if (rv)
135         oops...
136
137       /* Save the template-ID for later use */
138       mlm->template_id = template_id;
139
140 Several things are worth describing in more detail.
141
142 vnet_flow_rewrite_generic_callback programming
143 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
144
145 This generic callback helps build ipfix template packets. When
146 registering an ipfix report, pass an (array, count) of ipfix elements as
147 shown above.
148
149 my_flow_data_callback
150 ~~~~~~~~~~~~~~~~~~~~~
151
152 The ipfix flow export infrastructure calls this callback to flush the
153 current ipfix packet; to make sure that ipfix data is not retained for
154 an unreasonably long period of time.
155
156 We typically code it as shown below, to call an application-specific
157 function with (uninteresting arguments), and “do_flush = 1”:
158
159 .. code:: c
160
161
162          vlib_frame_t *my_flow_data_callback
163                       (flow_report_main_t * frm,
164                   flow_report_t * fr,
165               vlib_frame_t * f,
166               u32 * to_next, u32 node_index)
167          {
168
169             my_buffer_flow_record (0, ... , 0, 1 /* do_flush */);
170             return f;
171          }
172
173 my_flow_data_header
174 ~~~~~~~~~~~~~~~~~~~
175
176 This function creates the packet header for an ipfix data packet
177
178 .. code:: c
179
180
181       static inline void
182       my_flow_report_header (flow_report_main_t * frm,
183                  vlib_buffer_t * b0, u32 * offset)
184       {
185          my_logging_main_t *mlm = &my_logging_main;
186          flow_report_stream_t *stream;
187          ip4_ipfix_template_packet_t *tp;
188          ipfix_message_header_t *h = 0;
189
190
191          ipfix_set_header_t *s = 0;
192          ip4_header_t *ip;
193          udp_header_t *udp;
194
195          stream = &frm->streams[mlm->stream_index];
196
197          b0->current_data = 0;
198          b0->current_length = sizeof (*ip) + sizeof (*udp) + sizeof (*h) +
199            sizeof (*s);
200          b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_FLOW_REPORT);
201          vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
202          vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index;
203          tp = vlib_buffer_get_current (b0);
204          ip = (ip4_header_t *) & tp->ip4;
205          udp = (udp_header_t *) (ip + 1);
206          h = (ipfix_message_header_t *) (udp + 1);
207          s = (ipfix_set_header_t *) (h + 1);
208
209          ip->ip_version_and_header_length = 0x45;
210          ip->ttl = 254;
211          ip->protocol = IP_PROTOCOL_UDP;
212          ip->flags_and_fragment_offset = 0;
213          ip->src_address.as_u32 = frm->src_address.as_u32;
214          ip->dst_address.as_u32 = frm->ipfix_collector.as_u32;
215          udp->src_port = clib_host_to_net_u16 (stream->src_port);
216          udp->dst_port = clib_host_to_net_u16 (frm->collector_port);
217          udp->checksum = 0;
218
219          h->export_time = clib_host_to_net_u32 ((u32)
220                                 (((f64) frm->unix_time_0) +
221                                  (vlib_time_now (frm->vlib_main) -
222                                   frm->vlib_time_0)));
223             h->sequence_number = clib_host_to_net_u32 (stream->sequence_number++);
224             h->domain_id = clib_host_to_net_u32 (stream->domain_id);
225
226             *offset = (u32) (((u8 *) (s + 1)) - (u8 *) tp);
227       }
228
229 ### fixup and transmit a flow record
230
231 .. code:: c
232
233
234       static inline void
235       my_send_ipfix_pkt (flow_report_main_t * frm,
236                  vlib_frame_t * f, vlib_buffer_t * b0, u16 template_id)
237       {
238         ip4_ipfix_template_packet_t *tp;
239         ipfix_message_header_t *h = 0;
240         ipfix_set_header_t *s = 0;
241         ip4_header_t *ip;
242         udp_header_t *udp;
243         vlib_main_t *vm = frm->vlib_main;
244
245         tp = vlib_buffer_get_current (b0);
246         ip = (ip4_header_t *) & tp->ip4;
247         udp = (udp_header_t *) (ip + 1);
248         h = (ipfix_message_header_t *) (udp + 1);
249         s = (ipfix_set_header_t *) (h + 1);
250
251         s->set_id_length = ipfix_set_id_length (template_id,
252                           b0->current_length -
253                           (sizeof (*ip) + sizeof (*udp) +
254                            sizeof (*h)));
255         h->version_length = version_length (b0->current_length -
256                           (sizeof (*ip) + sizeof (*udp)));
257
258         ip->length = clib_host_to_net_u16 (b0->current_length);
259         ip->checksum = ip4_header_checksum (ip);
260         udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
261
262         if (frm->udp_checksum)
263           {
264             udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
265             if (udp->checksum == 0)
266         udp->checksum = 0xffff;
267           }
268
269         ASSERT (ip4_header_checksum_is_valid (ip));
270
271         vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
272       }
273
274 ### my_buffer_flow_record
275
276 This is the key routine which paints individual flow records into an
277 ipfix packet under construction. It’s pretty straightforward (albeit
278 stateful) vpp data-plane code. The code shown below is thread-safe by
279 construction.
280
281 .. code:: c
282
283    static inline void
284    my_buffer_flow_record_internal (my_flow_record_t * rp, int do_flush,
285                                        u32 thread_index)
286    {
287      vlib_main_t *vm = vlib_mains[thread_index];
288      my_logging_main_t *mlm = &jvp_ipfix_main;
289      flow_report_main_t *frm = &flow_report_main;
290      vlib_frame_t *f;
291      vlib_buffer_t *b0 = 0;
292      u32 bi0 = ~0;
293      u32 offset;
294
295      b0 = mlm->buffers_by_thread[thread_index];
296
297      if (PREDICT_FALSE (b0 == 0))
298        {
299          if (do_flush)
300     return;
301
302          if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
303     {
304       clib_warning ("can't allocate ipfix data buffer");
305       return;
306     }
307
308          b0 = vlib_get_buffer (vm, bi0);
309          offset = 0;
310          mlm->buffers_by_thread[thread_index] = b0;
311        }
312      else
313        {
314          bi0 = vlib_get_buffer_index (vm, b0);
315          offset = mlm->next_record_offset_by_thread[thread_index];
316        }
317
318      f = mlm->frames_by_thread[thread_index];
319      if (PREDICT_FALSE (f == 0))
320        {
321          u32 *to_next;
322          f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
323          mlm->frames_by_thread[thread_index] = f;
324          to_next = vlib_frame_vector_args (f);
325          to_next[0] = bi0;
326          f->n_vectors = 1;
327          mlm->frames_by_thread[thread_index] = f;
328        }
329
330      if (PREDICT_FALSE (offset == 0))
331        my_flow_report_header (frm, b0, &offset);
332
333      if (PREDICT_TRUE (do_flush == 0))
334        {
335          /* Paint the new ipfix data record into the buffer */
336          clib_memcpy (b0->data + offset, rp, sizeof (*rp));
337          offset += sizeof (*rp);
338          b0->current_length += sizeof (*rp);
339        }
340
341      if (PREDICT_FALSE (do_flush || (offset + sizeof (*rp)) > frm->path_mtu))
342        {
343          /* Nothing to send? */
344          if (offset == 0)
345     return;
346
347          send_ipfix_pkt (frm, f, b0, mlm->template_ids[0]);
348          mlm->buffers_by_thread[thread_index] = 0;
349          mlm->frames_by_thread[thread_index] = 0;
350          offset = 0;
351        }
352      mlm->next_record_offset_by_thread[thread_index] = offset;
353    }
354
355    static void
356    my_buffer_flow_record (my_flow_record_t * rp, int do_flush)
357    {
358      u32 thread_index = vlib_get_thread_index();
359      my_buffer_flow_record_internal (rp, do_flush, thread_index);
360    }