Doxygen cleanup.
[vpp.git] / src / vnet / ipfix-export / ipfix_doc.md
1 # IPFIX support {#ipfix_doc}
2
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.
6
7 As you'll see, a bit of typing is required. 
8
9 ## First: create an ipfix "report"
10
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.
13
14 ```{.c}
15    #include <vnet/ipfix-export/flow_report.h>
16    /* Defined in flow_report.h, of interest when constructing reports */
17
18    /* ipfix field definitions for a particular report */
19    typedef struct
20    {
21      u32 info_element;
22      u32 size;
23    } ipfix_report_element_t;
24
25    /* Report add/del argument structure */
26    typedef struct
27    {
28      /* Callback to flush current ipfix packet / frame */
29      vnet_flow_data_callback_t *flow_data_callback;
30
31      /* Callback to build the template packet rewrite string */
32      vnet_flow_rewrite_callback_t *rewrite_callback;
33
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 */
38      opaque_t opaque;
39      /* Add / delete a report */
40      int is_add;
41      /* Ipfix "domain-ID", see RFC, set as desired */
42      u32 domain_id;
43      /* ipfix packet source port, often set to UDP_DST_PORT_ipfix */
44      u16 src_port;
45      /* Set by ipfix infra, needed to send data packets */
46      u32 *stream_indexp;
47    } vnet_flow_report_add_del_args_t;
48
49    /* Private header file contents */
50
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)
60
61    static ipfix_report_element_t simple_report_elements[] = {
62    #define _(a,b) {a,b},
63      foreach_simple_report_ipfix_element
64    #undef _
65    };
66
67    typedef struct
68    {
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;
73
74      /** Template ID's */
75      u16 *template_ids;
76
77      /** Time reference pair */
78      u64 usec_time_0;
79      f64 vlib_time_0;
80
81      /** Stream index */
82      u32 stream_index;
83
84      /* Convenience */
85      flow_report_main_t *flow_report_main;
86      vlib_main_t *vlib_main;
87      vnet_main_t *vnet_main;
88    } my_logging_main_t;
89    
90    extern my_logging_main_t my_logging_main;
91
92    ...
93
94    /* Recitations */
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;
98    int rv;
99    u16 template_id;
100
101    ... 
102
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;
106
107    ...
108
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 */;
114
115    /* Use the generic template packet rewrite string generator */
116    a.rewrite_callback = vnet_flow_rewrite_generic_callback;
117
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);
121
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;
125
126    /* Create the report */
127    rv = vnet_flow_report_add_del (frm, &a, &template_id);
128    if (rv) 
129      oops...
130
131    /* Save the template-ID for later use */
132    mlm->template_id = template_id;
133
134 ```
135
136 Several things are worth describing in more detail.
137
138 ### vnet_flow_rewrite_generic_callback programming
139
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. 
143
144 ### my_flow_data_callback
145
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.
149
150 We typically code it as shown below, to call an application-specific
151 function with (uninteresting arguments), and "do_flush = 1":
152
153
154 ```{.c}
155
156       vlib_frame_t *my_flow_data_callback
157                    (flow_report_main_t * frm,
158                    flow_report_t * fr,
159                    vlib_frame_t * f,
160                    u32 * to_next, u32 node_index)
161       { 
162
163          my_buffer_flow_record (0, ... , 0, 1 /* do_flush */);
164          return f;
165       }
166 ```
167
168 ### my_flow_data_header
169
170 This function creates the packet header for an ipfix data packet
171
172 ```{.c}
173
174    static inline void
175    my_flow_report_header (flow_report_main_t * frm,
176                           vlib_buffer_t * b0, u32 * offset)
177    {
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;
182
183
184       ipfix_set_header_t *s = 0;
185       ip4_header_t *ip;
186       udp_header_t *udp;
187
188       stream = &frm->streams[mlm->stream_index];
189
190       b0->current_data = 0;
191       b0->current_length = sizeof (*ip) + sizeof (*udp) + sizeof (*h) +
192         sizeof (*s);
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);
201
202       ip->ip_version_and_header_length = 0x45;
203       ip->ttl = 254;
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);
210       udp->checksum = 0;
211
212       h->export_time = clib_host_to_net_u32 ((u32)
213                                          (((f64) frm->unix_time_0) +
214                                           (vlib_time_now (frm->vlib_main) -
215                                            frm->vlib_time_0)));
216          h->sequence_number = clib_host_to_net_u32 (stream->sequence_number++);
217          h->domain_id = clib_host_to_net_u32 (stream->domain_id);
218
219          *offset = (u32) (((u8 *) (s + 1)) - (u8 *) tp);
220    }
221    ```
222
223    ### fixup and transmit a flow record
224
225    ```{.c}
226       
227       static inline void
228       my_send_ipfix_pkt (flow_report_main_t * frm,
229                          vlib_frame_t * f, vlib_buffer_t * b0, u16 template_id)
230       {
231         ip4_ipfix_template_packet_t *tp;
232         ipfix_message_header_t *h = 0;
233         ipfix_set_header_t *s = 0;
234         ip4_header_t *ip;
235         udp_header_t *udp;
236         vlib_main_t *vm = frm->vlib_main;
237
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);
243
244         s->set_id_length = ipfix_set_id_length (template_id,
245                                           b0->current_length -
246                                           (sizeof (*ip) + sizeof (*udp) +
247                                            sizeof (*h)));
248         h->version_length = version_length (b0->current_length -
249                                       (sizeof (*ip) + sizeof (*udp)));
250
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));
254
255         if (frm->udp_checksum)
256           {
257             udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
258             if (udp->checksum == 0)
259         udp->checksum = 0xffff;
260           }
261
262         ASSERT (ip->checksum == ip4_header_checksum (ip));
263
264         vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
265       }  
266    ```
267
268    ### my_buffer_flow_record
269
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.
274
275    ```{.c}
276    static inline void
277    my_buffer_flow_record_internal (my_flow_record_t * rp, int do_flush,
278                                        u32 thread_index)
279    {
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;
283      vlib_frame_t *f;
284      vlib_buffer_t *b0 = 0;
285      u32 bi0 = ~0;
286      u32 offset;
287
288      b0 = mlm->buffers_by_thread[thread_index];
289
290      if (PREDICT_FALSE (b0 == 0))
291        {
292          if (do_flush)
293         return;
294
295          if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
296         {
297           clib_warning ("can't allocate ipfix data buffer");
298           return;
299         }
300
301          b0 = vlib_get_buffer (vm, bi0);
302          VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
303          offset = 0;
304          mlm->buffers_by_thread[thread_index] = b0;
305        }
306      else
307        {
308          bi0 = vlib_get_buffer_index (vm, b0);
309          offset = mlm->next_record_offset_by_thread[thread_index];
310        }
311
312      f = mlm->frames_by_thread[thread_index];
313      if (PREDICT_FALSE (f == 0))
314        {
315          u32 *to_next;
316          f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
317          mlm->frames_by_thread[thread_index] = f;
318          to_next = vlib_frame_vector_args (f);
319          to_next[0] = bi0;
320          f->n_vectors = 1;
321          mlm->frames_by_thread[thread_index] = f;
322        }
323
324      if (PREDICT_FALSE (offset == 0))
325        my_flow_report_header (frm, b0, &offset);
326
327      if (PREDICT_TRUE (do_flush == 0))
328        {
329          /* Paint the new ipfix data record into the buffer */
330          clib_memcpy (b0->data + offset, rp, sizeof (*rp));
331          offset += sizeof (*rp);
332          b0->current_length += sizeof (*rp);
333        }
334
335      if (PREDICT_FALSE (do_flush || (offset + sizeof (*rp)) > frm->path_mtu))
336        {
337          /* Nothing to send? */
338          if (offset == 0)
339         return;
340
341          send_ipfix_pkt (frm, f, b0, mlm->template_ids[0]);
342          mlm->buffers_by_thread[thread_index] = 0;
343          mlm->frames_by_thread[thread_index] = 0;
344          offset = 0;
345        }
346      mlm->next_record_offset_by_thread[thread_index] = offset;
347    }  
348
349    static void
350    my_buffer_flow_record (my_flow_record_t * rp, int do_flush)
351    {
352      u32 thread_index = vlib_get_thread_index();
353      my_buffer_flow_record_internal (rp, do_flush, thread_index);
354    }  
355
356 ```