ipfix-export: refactor params to the callback fns
[vpp.git] / src / vnet / ipfix-export / flow_report.c
1 /*
2  * Copyright (c) 2015 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16  * flow_report.c
17  */
18 #include <vppinfra/atomics.h>
19 #include <vnet/ipfix-export/flow_report.h>
20 #include <vnet/api_errno.h>
21 #include <vnet/udp/udp.h>
22
23 flow_report_main_t flow_report_main;
24
25 static_always_inline u8
26 stream_index_valid (ipfix_exporter_t *exp, u32 index)
27 {
28   return index < vec_len (exp->streams) && exp->streams[index].domain_id != ~0;
29 }
30
31 static_always_inline flow_report_stream_t *
32 add_stream (ipfix_exporter_t *exp)
33 {
34   u32 i;
35   for (i = 0; i < vec_len (exp->streams); i++)
36     if (!stream_index_valid (exp, i))
37       return &exp->streams[i];
38   u32 index = vec_len (exp->streams);
39   vec_validate (exp->streams, index);
40   return &exp->streams[index];
41 }
42
43 static_always_inline void
44 delete_stream (ipfix_exporter_t *exp, u32 index)
45 {
46   ASSERT (index < vec_len (exp->streams));
47   ASSERT (exp->streams[index].domain_id != ~0);
48   exp->streams[index].domain_id = ~0;
49 }
50
51 static i32
52 find_stream (ipfix_exporter_t *exp, u32 domain_id, u16 src_port)
53 {
54   flow_report_stream_t *stream;
55   u32 i;
56   for (i = 0; i < vec_len (exp->streams); i++)
57     if (stream_index_valid (exp, i))
58       {
59         stream = &exp->streams[i];
60         if (domain_id == stream->domain_id)
61           {
62             if (src_port != stream->src_port)
63               return -2;
64             return i;
65           }
66         else if (src_port == stream->src_port)
67           {
68             return -2;
69           }
70       }
71   return -1;
72 }
73
74 int
75 send_template_packet (flow_report_main_t *frm, ipfix_exporter_t *exp,
76                       flow_report_t *fr, u32 *buffer_indexp)
77 {
78   u32 bi0;
79   vlib_buffer_t *b0;
80   ip4_ipfix_template_packet_t *tp;
81   ipfix_message_header_t *h;
82   ip4_header_t *ip;
83   udp_header_t *udp;
84   vlib_main_t *vm = frm->vlib_main;
85   flow_report_stream_t *stream;
86
87   ASSERT (buffer_indexp);
88
89   if (fr->update_rewrite || fr->rewrite == 0)
90     {
91       if (exp->ipfix_collector.as_u32 == 0 || exp->src_address.as_u32 == 0)
92         {
93           vlib_node_set_state (frm->vlib_main, flow_report_process_node.index,
94                                VLIB_NODE_STATE_DISABLED);
95           return -1;
96         }
97       vec_free (fr->rewrite);
98       fr->update_rewrite = 1;
99     }
100
101   if (fr->update_rewrite)
102     {
103       fr->rewrite = fr->rewrite_callback (
104         exp, fr, exp->collector_port, fr->report_elements,
105         fr->n_report_elements, fr->stream_indexp);
106       fr->update_rewrite = 0;
107     }
108
109   if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
110     return -1;
111
112   b0 = vlib_get_buffer (vm, bi0);
113
114   ASSERT (vec_len (fr->rewrite) < vlib_buffer_get_default_data_size (vm));
115
116   clib_memcpy_fast (b0->data, fr->rewrite, vec_len (fr->rewrite));
117   b0->current_data = 0;
118   b0->current_length = vec_len (fr->rewrite);
119   b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_FLOW_REPORT);
120   vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
121   vnet_buffer (b0)->sw_if_index[VLIB_TX] = exp->fib_index;
122
123   tp = vlib_buffer_get_current (b0);
124   ip = (ip4_header_t *) & tp->ip4;
125   udp = (udp_header_t *) (ip + 1);
126   h = (ipfix_message_header_t *) (udp + 1);
127
128   /* FIXUP: message header export_time */
129   h->export_time = (u32)
130     (((f64) frm->unix_time_0) +
131      (vlib_time_now (frm->vlib_main) - frm->vlib_time_0));
132   h->export_time = clib_host_to_net_u32 (h->export_time);
133
134   stream = &exp->streams[fr->stream_index];
135
136   /* FIXUP: message header sequence_number. Templates do not increase it */
137   h->sequence_number = clib_host_to_net_u32 (stream->sequence_number);
138
139   /* FIXUP: udp length */
140   udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
141
142   if (exp->udp_checksum)
143     {
144       /* RFC 7011 section 10.3.2. */
145       udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
146       if (udp->checksum == 0)
147         udp->checksum = 0xffff;
148     }
149
150   *buffer_indexp = bi0;
151
152   fr->last_template_sent = vlib_time_now (vm);
153
154   return 0;
155 }
156
157 u8 *
158 vnet_flow_rewrite_generic_callback (ipfix_exporter_t *exp, flow_report_t *fr,
159                                     u16 collector_port,
160                                     ipfix_report_element_t *report_elts,
161                                     u32 n_elts, u32 *stream_indexp)
162 {
163   ip4_header_t *ip;
164   udp_header_t *udp;
165   ipfix_message_header_t *h;
166   ipfix_set_header_t *s;
167   ipfix_template_header_t *t;
168   ipfix_field_specifier_t *f;
169   ipfix_field_specifier_t *first_field;
170   u8 *rewrite = 0;
171   ip4_ipfix_template_packet_t *tp;
172   flow_report_stream_t *stream;
173   int i;
174   ipfix_report_element_t *ep;
175
176   ASSERT (stream_indexp);
177   ASSERT (n_elts);
178   ASSERT (report_elts);
179
180   stream = &exp->streams[fr->stream_index];
181   *stream_indexp = fr->stream_index;
182
183   /* allocate rewrite space */
184   vec_validate_aligned (rewrite,
185                         sizeof (ip4_ipfix_template_packet_t)
186                         + n_elts * sizeof (ipfix_field_specifier_t) - 1,
187                         CLIB_CACHE_LINE_BYTES);
188
189   /* create the packet rewrite string */
190   tp = (ip4_ipfix_template_packet_t *) rewrite;
191   ip = (ip4_header_t *) & tp->ip4;
192   udp = (udp_header_t *) (ip + 1);
193   h = (ipfix_message_header_t *) (udp + 1);
194   s = (ipfix_set_header_t *) (h + 1);
195   t = (ipfix_template_header_t *) (s + 1);
196   first_field = f = (ipfix_field_specifier_t *) (t + 1);
197
198   ip->ip_version_and_header_length = 0x45;
199   ip->ttl = 254;
200   ip->protocol = IP_PROTOCOL_UDP;
201   ip->src_address.as_u32 = exp->src_address.as_u32;
202   ip->dst_address.as_u32 = exp->ipfix_collector.as_u32;
203   udp->src_port = clib_host_to_net_u16 (stream->src_port);
204   udp->dst_port = clib_host_to_net_u16 (collector_port);
205   udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip));
206
207   /* FIXUP LATER: message header export_time */
208   h->domain_id = clib_host_to_net_u32 (stream->domain_id);
209
210   ep = report_elts;
211
212   for (i = 0; i < n_elts; i++)
213     {
214       f->e_id_length = ipfix_e_id_length (0, ep->info_element, ep->size);
215       f++;
216       ep++;
217     }
218
219   /* Back to the template packet... */
220   ip = (ip4_header_t *) & tp->ip4;
221   udp = (udp_header_t *) (ip + 1);
222
223   ASSERT (f - first_field);
224   /* Field count in this template */
225   t->id_count = ipfix_id_count (fr->template_id, f - first_field);
226
227   /* set length in octets */
228   s->set_id_length =
229     ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
230
231   /* message length in octets */
232   h->version_length = version_length ((u8 *) f - (u8 *) h);
233
234   ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip);
235   ip->checksum = ip4_header_checksum (ip);
236
237   return rewrite;
238 }
239
240 vlib_buffer_t *
241 vnet_ipfix_exp_get_buffer (vlib_main_t *vm, ipfix_exporter_t *exp,
242                            flow_report_t *fr, u32 thread_index)
243 {
244   u32 bi0;
245   vlib_buffer_t *b0;
246
247   if (fr->per_thread_data[thread_index].buffer)
248     return fr->per_thread_data[thread_index].buffer;
249
250   if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
251     return NULL;
252
253   /* Initialize the buffer */
254   b0 = fr->per_thread_data[thread_index].buffer = vlib_get_buffer (vm, bi0);
255
256   b0->current_data = 0;
257   b0->current_length = exp->all_headers_size;
258   b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_FLOW_REPORT);
259   vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
260   vnet_buffer (b0)->sw_if_index[VLIB_TX] = exp->fib_index;
261   fr->per_thread_data[thread_index].next_data_offset = b0->current_length;
262
263   return b0;
264 }
265
266 /*
267  * Send a buffer that is mostly populated. Has flow records but needs some
268  * header fields updated.
269  */
270 void
271 vnet_ipfix_exp_send_buffer (vlib_main_t *vm, ipfix_exporter_t *exp,
272                             flow_report_t *fr, flow_report_stream_t *stream,
273                             u32 thread_index, vlib_buffer_t *b0)
274 {
275   flow_report_main_t *frm = &flow_report_main;
276   vlib_frame_t *f;
277   ip4_ipfix_template_packet_t *tp;
278   ipfix_set_header_t *s;
279   ipfix_message_header_t *h;
280   ip4_header_t *ip;
281   udp_header_t *udp;
282
283   /* nothing to send */
284   if (fr->per_thread_data[thread_index].next_data_offset <=
285       exp->all_headers_size)
286     return;
287
288   tp = vlib_buffer_get_current (b0);
289   ip = (ip4_header_t *) &tp->ip4;
290   udp = (udp_header_t *) (ip + 1);
291   h = (ipfix_message_header_t *) (udp + 1);
292   s = (ipfix_set_header_t *) (h + 1);
293
294   ip->ip_version_and_header_length = 0x45;
295   ip->ttl = 254;
296   ip->protocol = IP_PROTOCOL_UDP;
297   ip->flags_and_fragment_offset = 0;
298   ip->src_address.as_u32 = exp->src_address.as_u32;
299   ip->dst_address.as_u32 = exp->ipfix_collector.as_u32;
300   udp->src_port = clib_host_to_net_u16 (stream->src_port);
301   udp->dst_port = clib_host_to_net_u16 (exp->collector_port);
302   udp->checksum = 0;
303
304   /* FIXUP: message header export_time */
305   h->export_time =
306     (u32) (((f64) frm->unix_time_0) + (vlib_time_now (vm) - frm->vlib_time_0));
307   h->export_time = clib_host_to_net_u32 (h->export_time);
308   h->domain_id = clib_host_to_net_u32 (stream->domain_id);
309
310   /*
311    * RFC 7011: Section 3.2
312    *
313    * Incremental sequence counter modulo 2^32 of all IPFIX Data Records
314    * sent in the current stream from the current Observation Domain by
315    * the Exporting Process
316    */
317   h->sequence_number =
318     clib_atomic_fetch_add (&stream->sequence_number,
319                            fr->per_thread_data[thread_index].n_data_records);
320   h->sequence_number = clib_host_to_net_u32 (h->sequence_number);
321
322   /*
323    * For data records we use the template ID as the set ID.
324    * RFC 7011: 3.4.3
325    */
326   s->set_id_length = ipfix_set_id_length (
327     fr->template_id,
328     b0->current_length - (sizeof (*ip) + sizeof (*udp) + sizeof (*h)));
329   h->version_length =
330     version_length (b0->current_length - (sizeof (*ip) + sizeof (*udp)));
331
332   ip->length = clib_host_to_net_u16 (b0->current_length);
333
334   ip->checksum = ip4_header_checksum (ip);
335   udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
336
337   if (exp->udp_checksum)
338     {
339       /* RFC 7011 section 10.3.2. */
340       udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
341       if (udp->checksum == 0)
342         udp->checksum = 0xffff;
343     }
344
345   ASSERT (ip4_header_checksum_is_valid (ip));
346
347   /* Find or allocate a frame */
348   f = fr->per_thread_data[thread_index].frame;
349   if (PREDICT_FALSE (f == 0))
350     {
351       u32 *to_next;
352       f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
353       fr->per_thread_data[thread_index].frame = f;
354       u32 bi0 = vlib_get_buffer_index (vm, b0);
355
356       /* Enqueue the buffer */
357       to_next = vlib_frame_vector_args (f);
358       to_next[0] = bi0;
359       f->n_vectors = 1;
360     }
361
362   vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
363
364   fr->per_thread_data[thread_index].frame = NULL;
365   fr->per_thread_data[thread_index].buffer = NULL;
366   fr->per_thread_data[thread_index].next_data_offset = 0;
367 }
368
369 static uword
370 flow_report_process (vlib_main_t * vm,
371                      vlib_node_runtime_t * rt, vlib_frame_t * f)
372 {
373   flow_report_main_t *frm = &flow_report_main;
374   flow_report_t *fr;
375   u32 ip4_lookup_node_index;
376   vlib_node_t *ip4_lookup_node;
377   vlib_frame_t *nf = 0;
378   u32 template_bi;
379   u32 *to_next;
380   int send_template;
381   f64 now, wait_time;
382   f64 def_wait_time = 5.0;
383   int rv;
384   uword event_type;
385   uword *event_data = 0;
386
387   /* Wait for Godot... */
388   vlib_process_wait_for_event_or_clock (vm, 1e9);
389   event_type = vlib_process_get_events (vm, &event_data);
390   if (event_type != 1)
391     clib_warning ("bogus kickoff event received, %d", event_type);
392   vec_reset_length (event_data);
393
394   /* Enqueue pkts to ip4-lookup */
395   ip4_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip4-lookup");
396   ip4_lookup_node_index = ip4_lookup_node->index;
397
398   wait_time = def_wait_time;
399
400   while (1)
401     {
402       vlib_process_wait_for_event_or_clock (vm, wait_time);
403       event_type = vlib_process_get_events (vm, &event_data);
404       vec_reset_length (event_data);
405       ipfix_exporter_t *exp;
406       pool_foreach (exp, frm->exporters)
407         {
408
409           /* 5s delay by default, possibly reduced by template intervals */
410           wait_time = def_wait_time;
411
412           vec_foreach (fr, exp->reports)
413             {
414               f64 next_template;
415               now = vlib_time_now (vm);
416
417               /* Need to send a template packet? */
418               send_template =
419                 now > (fr->last_template_sent + exp->template_interval);
420               send_template += fr->last_template_sent == 0;
421               template_bi = ~0;
422               rv = 0;
423
424               if (send_template)
425                 rv = send_template_packet (frm, exp, fr, &template_bi);
426
427               if (rv < 0)
428                 continue;
429
430               /*
431                * decide if template should be sent sooner than current wait
432                * time
433                */
434               next_template =
435                 (fr->last_template_sent + exp->template_interval) - now;
436               wait_time = clib_min (wait_time, next_template);
437
438               nf = vlib_get_frame_to_node (vm, ip4_lookup_node_index);
439               nf->n_vectors = 0;
440               to_next = vlib_frame_vector_args (nf);
441
442               if (template_bi != ~0)
443                 {
444                   to_next[0] = template_bi;
445                   to_next++;
446                   nf->n_vectors++;
447                 }
448
449               nf = fr->flow_data_callback (frm, exp, fr, nf, to_next,
450                                            ip4_lookup_node_index);
451               if (nf)
452                 vlib_put_frame_to_node (vm, ip4_lookup_node_index, nf);
453             }
454         }
455     }
456
457   return 0;                     /* not so much */
458 }
459
460 /* *INDENT-OFF* */
461 VLIB_REGISTER_NODE (flow_report_process_node) = {
462     .function = flow_report_process,
463     .type = VLIB_NODE_TYPE_PROCESS,
464     .name = "flow-report-process",
465 };
466 /* *INDENT-ON* */
467
468 int
469 vnet_flow_report_add_del (ipfix_exporter_t *exp,
470                           vnet_flow_report_add_del_args_t *a, u16 *template_id)
471 {
472   int i;
473   int found_index = ~0;
474   flow_report_t *fr;
475   flow_report_stream_t *stream;
476   u32 si;
477   vlib_thread_main_t *tm = &vlib_thread_main;
478   flow_report_main_t *frm = &flow_report_main;
479   vlib_main_t *vm = frm->vlib_main;
480   int size;
481
482   si = find_stream (exp, a->domain_id, a->src_port);
483   if (si == -2)
484     return VNET_API_ERROR_INVALID_VALUE;
485   if (si == -1 && a->is_add == 0)
486     return VNET_API_ERROR_NO_SUCH_ENTRY;
487
488   for (i = 0; i < vec_len (exp->reports); i++)
489     {
490       fr = vec_elt_at_index (exp->reports, i);
491       if (fr->opaque.as_uword == a->opaque.as_uword
492           && fr->rewrite_callback == a->rewrite_callback
493           && fr->flow_data_callback == a->flow_data_callback)
494         {
495           found_index = i;
496           if (template_id)
497             *template_id = fr->template_id;
498           break;
499         }
500     }
501
502   if (a->is_add == 0)
503     {
504       if (found_index != ~0)
505         {
506           for (int i = 0;
507                i < vec_len (exp->reports[found_index].per_thread_data); i++)
508             {
509               u32 bi;
510               if (exp->reports[found_index].per_thread_data[i].buffer)
511                 {
512                   bi = vlib_get_buffer_index (
513                     vm, exp->reports[found_index].per_thread_data[i].buffer);
514                   vlib_buffer_free (vm, &bi, 1);
515                 }
516             }
517           vec_free (exp->reports[found_index].per_thread_data);
518
519           vec_delete (exp->reports, 1, found_index);
520           stream = &exp->streams[si];
521           stream->n_reports--;
522           if (stream->n_reports == 0)
523             delete_stream (exp, si);
524           return 0;
525         }
526       return VNET_API_ERROR_NO_SUCH_ENTRY;
527     }
528
529   if (found_index != ~0)
530     return VNET_API_ERROR_VALUE_EXIST;
531
532   if (si == -1)
533     {
534       stream = add_stream (exp);
535       stream->domain_id = a->domain_id;
536       stream->src_port = a->src_port;
537       stream->sequence_number = 0;
538       stream->n_reports = 0;
539       si = stream - exp->streams;
540     }
541   else
542     stream = &exp->streams[si];
543
544   stream->n_reports++;
545
546   vec_add2 (exp->reports, fr, 1);
547
548   fr->stream_index = si;
549   fr->template_id = 256 + stream->next_template_no;
550   stream->next_template_no = (stream->next_template_no + 1) % (65536 - 256);
551   fr->update_rewrite = 1;
552   fr->opaque = a->opaque;
553   fr->rewrite_callback = a->rewrite_callback;
554   fr->flow_data_callback = a->flow_data_callback;
555   fr->report_elements = a->report_elements;
556   fr->n_report_elements = a->n_report_elements;
557   fr->stream_indexp = a->stream_indexp;
558   vec_validate (fr->per_thread_data, tm->n_threads);
559   /* Store the flow_report index back in the args struct */
560   a->flow_report_index = fr - exp->reports;
561
562   size = 0;
563   for (int i = 0; i < fr->n_report_elements; i++)
564     size += fr->report_elements[i].size;
565   fr->data_record_size = size;
566   if (template_id)
567     *template_id = fr->template_id;
568
569   return 0;
570 }
571
572 clib_error_t *
573 flow_report_add_del_error_to_clib_error (int error)
574 {
575   switch (error)
576     {
577     case 0:
578       return 0;
579     case VNET_API_ERROR_NO_SUCH_ENTRY:
580       return clib_error_return (0, "Flow report not found");
581     case VNET_API_ERROR_VALUE_EXIST:
582       return clib_error_return (0, "Flow report already exists");
583     case VNET_API_ERROR_INVALID_VALUE:
584       return clib_error_return (0, "Expecting either still unused values "
585                                 "for both domain_id and src_port "
586                                 "or already used values for both fields");
587     default:
588       return clib_error_return (0, "vnet_flow_report_add_del returned %d",
589                                 error);
590     }
591 }
592
593 void
594 vnet_flow_reports_reset (ipfix_exporter_t *exp)
595 {
596   flow_report_t *fr;
597   u32 i;
598
599   for (i = 0; i < vec_len (exp->streams); i++)
600     if (stream_index_valid (exp, i))
601       exp->streams[i].sequence_number = 0;
602
603   vec_foreach (fr, exp->reports)
604     {
605       fr->update_rewrite = 1;
606       fr->last_template_sent = 0;
607     }
608 }
609
610 void
611 vnet_stream_reset (ipfix_exporter_t *exp, u32 stream_index)
612 {
613   flow_report_t *fr;
614
615   exp->streams[stream_index].sequence_number = 0;
616
617   vec_foreach (fr, exp->reports)
618     if (exp->reports->stream_index == stream_index)
619       {
620         fr->update_rewrite = 1;
621         fr->last_template_sent = 0;
622       }
623 }
624
625 int
626 vnet_stream_change (ipfix_exporter_t *exp, u32 old_domain_id, u16 old_src_port,
627                     u32 new_domain_id, u16 new_src_port)
628 {
629   i32 stream_index = find_stream (exp, old_domain_id, old_src_port);
630
631   if (stream_index < 0)
632     return 1;
633   flow_report_stream_t *stream = &exp->streams[stream_index];
634   stream->domain_id = new_domain_id;
635   stream->src_port = new_src_port;
636   if (old_domain_id != new_domain_id || old_src_port != new_src_port)
637     vnet_stream_reset (exp, stream_index);
638   return 0;
639 }
640
641 static clib_error_t *
642 set_ipfix_exporter_command_fn (vlib_main_t * vm,
643                                unformat_input_t * input,
644                                vlib_cli_command_t * cmd)
645 {
646   flow_report_main_t *frm = &flow_report_main;
647   ip4_address_t collector, src;
648   u16 collector_port = UDP_DST_PORT_ipfix;
649   u32 fib_id;
650   u32 fib_index = ~0;
651
652   collector.as_u32 = 0;
653   src.as_u32 = 0;
654   u32 path_mtu = 512;           // RFC 7011 section 10.3.3.
655   u32 template_interval = 20;
656   u8 udp_checksum = 0;
657   ipfix_exporter_t *exp = pool_elt_at_index (frm->exporters, 0);
658
659   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
660     {
661       if (unformat (input, "collector %U", unformat_ip4_address, &collector))
662         ;
663       else if (unformat (input, "port %U", unformat_udp_port,
664                          &collector_port))
665         ;
666       else if (unformat (input, "src %U", unformat_ip4_address, &src))
667         ;
668       else if (unformat (input, "fib-id %u", &fib_id))
669         {
670           ip4_main_t *im = &ip4_main;
671           uword *p = hash_get (im->fib_index_by_table_id, fib_id);
672           if (!p)
673             return clib_error_return (0, "fib ID %d doesn't exist\n", fib_id);
674           fib_index = p[0];
675         }
676       else if (unformat (input, "path-mtu %u", &path_mtu))
677         ;
678       else if (unformat (input, "template-interval %u", &template_interval))
679         ;
680       else if (unformat (input, "udp-checksum"))
681         udp_checksum = 1;
682       else
683         break;
684     }
685
686   if (collector.as_u32 != 0 && src.as_u32 == 0)
687     return clib_error_return (0, "src address required");
688
689   if (path_mtu > 1450 /* vpp does not support fragmentation */ )
690     return clib_error_return (0, "too big path-mtu value, maximum is 1450");
691
692   if (path_mtu < 68)
693     return clib_error_return (0, "too small path-mtu value, minimum is 68");
694
695   /* Calculate how much header data we need. */
696   exp->all_headers_size = sizeof (ip4_header_t) + sizeof (udp_header_t) +
697                           sizeof (ipfix_message_header_t) +
698                           sizeof (ipfix_set_header_t);
699
700   /* Reset report streams if we are reconfiguring IP addresses */
701   if (exp->ipfix_collector.as_u32 != collector.as_u32 ||
702       exp->src_address.as_u32 != src.as_u32 ||
703       exp->collector_port != collector_port)
704     vnet_flow_reports_reset (exp);
705
706   exp->ipfix_collector.as_u32 = collector.as_u32;
707   exp->collector_port = collector_port;
708   exp->src_address.as_u32 = src.as_u32;
709   exp->fib_index = fib_index;
710   exp->path_mtu = path_mtu;
711   exp->template_interval = template_interval;
712   exp->udp_checksum = udp_checksum;
713
714   if (collector.as_u32)
715     vlib_cli_output (vm,
716                      "Collector %U, src address %U, "
717                      "fib index %d, path MTU %u, "
718                      "template resend interval %us, "
719                      "udp checksum %s",
720                      format_ip4_address, exp->ipfix_collector,
721                      format_ip4_address, exp->src_address, fib_index, path_mtu,
722                      template_interval, udp_checksum ? "enabled" : "disabled");
723   else
724     vlib_cli_output (vm, "IPFIX Collector is disabled");
725
726   /* Turn on the flow reporting process */
727   vlib_process_signal_event (vm, flow_report_process_node.index, 1, 0);
728   return 0;
729 }
730
731 /* *INDENT-OFF* */
732 VLIB_CLI_COMMAND (set_ipfix_exporter_command, static) = {
733     .path = "set ipfix exporter",
734     .short_help = "set ipfix exporter "
735                   "collector <ip4-address> [port <port>] "
736                   "src <ip4-address> [fib-id <fib-id>] "
737                   "[path-mtu <path-mtu>] "
738                   "[template-interval <template-interval>] "
739                   "[udp-checksum]",
740     .function = set_ipfix_exporter_command_fn,
741 };
742 /* *INDENT-ON* */
743
744
745 static clib_error_t *
746 ipfix_flush_command_fn (vlib_main_t * vm,
747                         unformat_input_t * input, vlib_cli_command_t * cmd)
748 {
749   /* poke the flow reporting process */
750   vlib_process_signal_event (vm, flow_report_process_node.index, 1, 0);
751   return 0;
752 }
753
754 /* *INDENT-OFF* */
755 VLIB_CLI_COMMAND (ipfix_flush_command, static) = {
756     .path = "ipfix flush",
757     .short_help = "flush the current ipfix data [for make test]",
758     .function = ipfix_flush_command_fn,
759 };
760 /* *INDENT-ON* */
761
762 static clib_error_t *
763 flow_report_init (vlib_main_t * vm)
764 {
765   flow_report_main_t *frm = &flow_report_main;
766   ipfix_exporter_t *exp;
767
768   frm->vlib_main = vm;
769   frm->vnet_main = vnet_get_main ();
770   frm->unix_time_0 = time (0);
771   frm->vlib_time_0 = vlib_time_now (frm->vlib_main);
772   /*
773    * Make sure that we can always access the first exporter for
774    * backwards compatibility reasons.
775    */
776   pool_alloc (frm->exporters, IPFIX_EXPORTERS_MAX);
777   pool_get (frm->exporters, exp);
778   /* Verify that this is at index 0 */
779   ASSERT (frm->exporters == exp);
780   exp->fib_index = ~0;
781   return 0;
782 }
783
784 VLIB_INIT_FUNCTION (flow_report_init);
785 /*
786  * fd.io coding-style-patch-verification: ON
787  *
788  * Local Variables:
789  * eval: (c-set-style "gnu")
790  * End:
791  */