Remove c-11 memcpy checks from perf-critical code
[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 <vnet/ipfix-export/flow_report.h>
19 #include <vnet/api_errno.h>
20
21 flow_report_main_t flow_report_main;
22
23 static_always_inline u8
24 stream_index_valid (u32 index)
25 {
26   flow_report_main_t *frm = &flow_report_main;
27   return index < vec_len (frm->streams) &&
28     frm->streams[index].domain_id != ~0;
29 }
30
31 static_always_inline flow_report_stream_t *
32 add_stream (void)
33 {
34   flow_report_main_t *frm = &flow_report_main;
35   u32 i;
36   for (i = 0; i < vec_len (frm->streams); i++)
37     if (!stream_index_valid (i))
38       return &frm->streams[i];
39   u32 index = vec_len (frm->streams);
40   vec_validate (frm->streams, index);
41   return &frm->streams[index];
42 }
43
44 static_always_inline void
45 delete_stream (u32 index)
46 {
47   flow_report_main_t *frm = &flow_report_main;
48   ASSERT (index < vec_len (frm->streams));
49   ASSERT (frm->streams[index].domain_id != ~0);
50   frm->streams[index].domain_id = ~0;
51 }
52
53 static i32
54 find_stream (u32 domain_id, u16 src_port)
55 {
56   flow_report_main_t *frm = &flow_report_main;
57   flow_report_stream_t *stream;
58   u32 i;
59   for (i = 0; i < vec_len (frm->streams); i++)
60     if (stream_index_valid (i))
61       {
62         stream = &frm->streams[i];
63         if (domain_id == stream->domain_id)
64           {
65             if (src_port != stream->src_port)
66               return -2;
67             return i;
68           }
69         else if (src_port == stream->src_port)
70           {
71             return -2;
72           }
73       }
74   return -1;
75 }
76
77 int
78 send_template_packet (flow_report_main_t * frm,
79                       flow_report_t * fr, u32 * buffer_indexp)
80 {
81   u32 bi0;
82   vlib_buffer_t *b0;
83   ip4_ipfix_template_packet_t *tp;
84   ipfix_message_header_t *h;
85   ip4_header_t *ip;
86   udp_header_t *udp;
87   vlib_main_t *vm = frm->vlib_main;
88   flow_report_stream_t *stream;
89   vlib_buffer_free_list_t *fl;
90
91   ASSERT (buffer_indexp);
92
93   if (fr->update_rewrite || fr->rewrite == 0)
94     {
95       if (frm->ipfix_collector.as_u32 == 0 || frm->src_address.as_u32 == 0)
96         {
97           vlib_node_set_state (frm->vlib_main, flow_report_process_node.index,
98                                VLIB_NODE_STATE_DISABLED);
99           return -1;
100         }
101       vec_free (fr->rewrite);
102       fr->update_rewrite = 1;
103     }
104
105   if (fr->update_rewrite)
106     {
107       fr->rewrite = fr->rewrite_callback (frm, fr,
108                                           &frm->ipfix_collector,
109                                           &frm->src_address,
110                                           frm->collector_port,
111                                           fr->report_elements,
112                                           fr->n_report_elements,
113                                           fr->stream_indexp);
114       fr->update_rewrite = 0;
115     }
116
117   if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
118     return -1;
119
120   b0 = vlib_get_buffer (vm, bi0);
121
122   /* Initialize the buffer */
123   fl = vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX);
124   vlib_buffer_init_for_free_list (b0, fl);
125   VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
126
127   ASSERT (vec_len (fr->rewrite) < VLIB_BUFFER_DEFAULT_FREE_LIST_BYTES);
128
129   clib_memcpy_fast (b0->data, fr->rewrite, vec_len (fr->rewrite));
130   b0->current_data = 0;
131   b0->current_length = vec_len (fr->rewrite);
132   b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_FLOW_REPORT);
133   vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
134   vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index;
135
136   tp = vlib_buffer_get_current (b0);
137   ip = (ip4_header_t *) & tp->ip4;
138   udp = (udp_header_t *) (ip + 1);
139   h = (ipfix_message_header_t *) (udp + 1);
140
141   /* FIXUP: message header export_time */
142   h->export_time = (u32)
143     (((f64) frm->unix_time_0) +
144      (vlib_time_now (frm->vlib_main) - frm->vlib_time_0));
145   h->export_time = clib_host_to_net_u32 (h->export_time);
146
147   stream = &frm->streams[fr->stream_index];
148
149   /* FIXUP: message header sequence_number. Templates do not increase it */
150   h->sequence_number = clib_host_to_net_u32 (stream->sequence_number);
151
152   /* FIXUP: udp length */
153   udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
154
155   if (frm->udp_checksum)
156     {
157       /* RFC 7011 section 10.3.2. */
158       udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
159       if (udp->checksum == 0)
160         udp->checksum = 0xffff;
161     }
162
163   *buffer_indexp = bi0;
164
165   fr->last_template_sent = vlib_time_now (vm);
166
167   return 0;
168 }
169
170 u8 *
171 vnet_flow_rewrite_generic_callback (flow_report_main_t * frm,
172                                     flow_report_t * fr,
173                                     ip4_address_t * collector_address,
174                                     ip4_address_t * src_address,
175                                     u16 collector_port,
176                                     ipfix_report_element_t * report_elts,
177                                     u32 n_elts, u32 * stream_indexp)
178 {
179   ip4_header_t *ip;
180   udp_header_t *udp;
181   ipfix_message_header_t *h;
182   ipfix_set_header_t *s;
183   ipfix_template_header_t *t;
184   ipfix_field_specifier_t *f;
185   ipfix_field_specifier_t *first_field;
186   u8 *rewrite = 0;
187   ip4_ipfix_template_packet_t *tp;
188   flow_report_stream_t *stream;
189   int i;
190   ipfix_report_element_t *ep;
191
192   ASSERT (stream_indexp);
193   ASSERT (n_elts);
194   ASSERT (report_elts);
195
196   stream = &frm->streams[fr->stream_index];
197   *stream_indexp = fr->stream_index;
198
199   /* allocate rewrite space */
200   vec_validate_aligned (rewrite,
201                         sizeof (ip4_ipfix_template_packet_t)
202                         + n_elts * sizeof (ipfix_field_specifier_t) - 1,
203                         CLIB_CACHE_LINE_BYTES);
204
205   /* create the packet rewrite string */
206   tp = (ip4_ipfix_template_packet_t *) rewrite;
207   ip = (ip4_header_t *) & tp->ip4;
208   udp = (udp_header_t *) (ip + 1);
209   h = (ipfix_message_header_t *) (udp + 1);
210   s = (ipfix_set_header_t *) (h + 1);
211   t = (ipfix_template_header_t *) (s + 1);
212   first_field = f = (ipfix_field_specifier_t *) (t + 1);
213
214   ip->ip_version_and_header_length = 0x45;
215   ip->ttl = 254;
216   ip->protocol = IP_PROTOCOL_UDP;
217   ip->src_address.as_u32 = src_address->as_u32;
218   ip->dst_address.as_u32 = collector_address->as_u32;
219   udp->src_port = clib_host_to_net_u16 (stream->src_port);
220   udp->dst_port = clib_host_to_net_u16 (collector_port);
221   udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip));
222
223   /* FIXUP LATER: message header export_time */
224   h->domain_id = clib_host_to_net_u32 (stream->domain_id);
225
226   ep = report_elts;
227
228   for (i = 0; i < n_elts; i++)
229     {
230       f->e_id_length = ipfix_e_id_length (0, ep->info_element, ep->size);
231       f++;
232       ep++;
233     }
234
235   /* Back to the template packet... */
236   ip = (ip4_header_t *) & tp->ip4;
237   udp = (udp_header_t *) (ip + 1);
238
239   ASSERT (f - first_field);
240   /* Field count in this template */
241   t->id_count = ipfix_id_count (fr->template_id, f - first_field);
242
243   /* set length in octets */
244   s->set_id_length =
245     ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
246
247   /* message length in octets */
248   h->version_length = version_length ((u8 *) f - (u8 *) h);
249
250   ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip);
251   ip->checksum = ip4_header_checksum (ip);
252
253   return rewrite;
254 }
255
256 static uword
257 flow_report_process (vlib_main_t * vm,
258                      vlib_node_runtime_t * rt, vlib_frame_t * f)
259 {
260   flow_report_main_t *frm = &flow_report_main;
261   flow_report_t *fr;
262   u32 ip4_lookup_node_index;
263   vlib_node_t *ip4_lookup_node;
264   vlib_frame_t *nf = 0;
265   u32 template_bi;
266   u32 *to_next;
267   int send_template;
268   f64 now;
269   int rv;
270   uword event_type;
271   uword *event_data = 0;
272
273   /* Wait for Godot... */
274   vlib_process_wait_for_event_or_clock (vm, 1e9);
275   event_type = vlib_process_get_events (vm, &event_data);
276   if (event_type != 1)
277     clib_warning ("bogus kickoff event received, %d", event_type);
278   vec_reset_length (event_data);
279
280   /* Enqueue pkts to ip4-lookup */
281   ip4_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip4-lookup");
282   ip4_lookup_node_index = ip4_lookup_node->index;
283
284   while (1)
285     {
286       vlib_process_wait_for_event_or_clock (vm, 5.0);
287       event_type = vlib_process_get_events (vm, &event_data);
288       vec_reset_length (event_data);
289
290       vec_foreach (fr, frm->reports)
291       {
292         now = vlib_time_now (vm);
293
294         /* Need to send a template packet? */
295         send_template =
296           now > (fr->last_template_sent + frm->template_interval);
297         send_template += fr->last_template_sent == 0;
298         template_bi = ~0;
299         rv = 0;
300
301         if (send_template)
302           rv = send_template_packet (frm, fr, &template_bi);
303
304         if (rv < 0)
305           continue;
306
307         nf = vlib_get_frame_to_node (vm, ip4_lookup_node_index);
308         nf->n_vectors = 0;
309         to_next = vlib_frame_vector_args (nf);
310
311         if (template_bi != ~0)
312           {
313             to_next[0] = template_bi;
314             to_next++;
315             nf->n_vectors++;
316           }
317
318         nf = fr->flow_data_callback (frm, fr,
319                                      nf, to_next, ip4_lookup_node_index);
320         if (nf)
321           vlib_put_frame_to_node (vm, ip4_lookup_node_index, nf);
322       }
323     }
324
325   return 0;                     /* not so much */
326 }
327
328 /* *INDENT-OFF* */
329 VLIB_REGISTER_NODE (flow_report_process_node) = {
330     .function = flow_report_process,
331     .type = VLIB_NODE_TYPE_PROCESS,
332     .name = "flow-report-process",
333 };
334 /* *INDENT-ON* */
335
336 int
337 vnet_flow_report_add_del (flow_report_main_t * frm,
338                           vnet_flow_report_add_del_args_t * a,
339                           u16 * template_id)
340 {
341   int i;
342   int found_index = ~0;
343   flow_report_t *fr;
344   flow_report_stream_t *stream;
345   u32 si;
346
347   si = find_stream (a->domain_id, a->src_port);
348   if (si == -2)
349     return VNET_API_ERROR_INVALID_VALUE;
350   if (si == -1 && a->is_add == 0)
351     return VNET_API_ERROR_NO_SUCH_ENTRY;
352
353   for (i = 0; i < vec_len (frm->reports); i++)
354     {
355       fr = vec_elt_at_index (frm->reports, i);
356       if (fr->opaque.as_uword == a->opaque.as_uword
357           && fr->rewrite_callback == a->rewrite_callback
358           && fr->flow_data_callback == a->flow_data_callback)
359         {
360           found_index = i;
361           if (template_id)
362             *template_id = fr->template_id;
363           break;
364         }
365     }
366
367   if (a->is_add == 0)
368     {
369       if (found_index != ~0)
370         {
371           vec_delete (frm->reports, 1, found_index);
372           stream = &frm->streams[si];
373           stream->n_reports--;
374           if (stream->n_reports == 0)
375             delete_stream (si);
376           return 0;
377         }
378       return VNET_API_ERROR_NO_SUCH_ENTRY;
379     }
380
381   if (found_index != ~0)
382     return VNET_API_ERROR_VALUE_EXIST;
383
384   if (si == -1)
385     {
386       stream = add_stream ();
387       stream->domain_id = a->domain_id;
388       stream->src_port = a->src_port;
389       stream->sequence_number = 0;
390       stream->n_reports = 0;
391       si = stream - frm->streams;
392     }
393   else
394     stream = &frm->streams[si];
395
396   stream->n_reports++;
397
398   vec_add2 (frm->reports, fr, 1);
399
400   fr->stream_index = si;
401   fr->template_id = 256 + stream->next_template_no;
402   stream->next_template_no = (stream->next_template_no + 1) % (65536 - 256);
403   fr->update_rewrite = 1;
404   fr->opaque = a->opaque;
405   fr->rewrite_callback = a->rewrite_callback;
406   fr->flow_data_callback = a->flow_data_callback;
407   fr->report_elements = a->report_elements;
408   fr->n_report_elements = a->n_report_elements;
409   fr->stream_indexp = a->stream_indexp;
410   if (template_id)
411     *template_id = fr->template_id;
412
413   return 0;
414 }
415
416 clib_error_t *
417 flow_report_add_del_error_to_clib_error (int error)
418 {
419   switch (error)
420     {
421     case 0:
422       return 0;
423     case VNET_API_ERROR_NO_SUCH_ENTRY:
424       return clib_error_return (0, "Flow report not found");
425     case VNET_API_ERROR_VALUE_EXIST:
426       return clib_error_return (0, "Flow report already exists");
427     case VNET_API_ERROR_INVALID_VALUE:
428       return clib_error_return (0, "Expecting either still unused values "
429                                 "for both domain_id and src_port "
430                                 "or already used values for both fields");
431     default:
432       return clib_error_return (0, "vnet_flow_report_add_del returned %d",
433                                 error);
434     }
435 }
436
437 void
438 vnet_flow_reports_reset (flow_report_main_t * frm)
439 {
440   flow_report_t *fr;
441   u32 i;
442
443   for (i = 0; i < vec_len (frm->streams); i++)
444     if (stream_index_valid (i))
445       frm->streams[i].sequence_number = 0;
446
447   vec_foreach (fr, frm->reports)
448   {
449     fr->update_rewrite = 1;
450     fr->last_template_sent = 0;
451   }
452 }
453
454 void
455 vnet_stream_reset (flow_report_main_t * frm, u32 stream_index)
456 {
457   flow_report_t *fr;
458
459   frm->streams[stream_index].sequence_number = 0;
460
461   vec_foreach (fr, frm->reports)
462     if (frm->reports->stream_index == stream_index)
463     {
464       fr->update_rewrite = 1;
465       fr->last_template_sent = 0;
466     }
467 }
468
469 int
470 vnet_stream_change (flow_report_main_t * frm,
471                     u32 old_domain_id, u16 old_src_port,
472                     u32 new_domain_id, u16 new_src_port)
473 {
474   i32 stream_index = find_stream (old_domain_id, old_src_port);
475   if (stream_index < 0)
476     return 1;
477   flow_report_stream_t *stream = &frm->streams[stream_index];
478   stream->domain_id = new_domain_id;
479   stream->src_port = new_src_port;
480   if (old_domain_id != new_domain_id || old_src_port != new_src_port)
481     vnet_stream_reset (frm, stream_index);
482   return 0;
483 }
484
485 static clib_error_t *
486 set_ipfix_exporter_command_fn (vlib_main_t * vm,
487                                unformat_input_t * input,
488                                vlib_cli_command_t * cmd)
489 {
490   flow_report_main_t *frm = &flow_report_main;
491   ip4_address_t collector, src;
492   u16 collector_port = UDP_DST_PORT_ipfix;
493   u32 fib_id;
494   u32 fib_index = ~0;
495
496   collector.as_u32 = 0;
497   src.as_u32 = 0;
498   u32 path_mtu = 512;           // RFC 7011 section 10.3.3.
499   u32 template_interval = 20;
500   u8 udp_checksum = 0;
501
502   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
503     {
504       if (unformat (input, "collector %U", unformat_ip4_address, &collector))
505         ;
506       else if (unformat (input, "port %u", &collector_port))
507         ;
508       else if (unformat (input, "src %U", unformat_ip4_address, &src))
509         ;
510       else if (unformat (input, "fib-id %u", &fib_id))
511         {
512           ip4_main_t *im = &ip4_main;
513           uword *p = hash_get (im->fib_index_by_table_id, fib_id);
514           if (!p)
515             return clib_error_return (0, "fib ID %d doesn't exist\n", fib_id);
516           fib_index = p[0];
517         }
518       else if (unformat (input, "path-mtu %u", &path_mtu))
519         ;
520       else if (unformat (input, "template-interval %u", &template_interval))
521         ;
522       else if (unformat (input, "udp-checksum"))
523         udp_checksum = 1;
524       else
525         break;
526     }
527
528   if (collector.as_u32 != 0 && src.as_u32 == 0)
529     return clib_error_return (0, "src address required");
530
531   if (path_mtu > 1450 /* vpp does not support fragmentation */ )
532     return clib_error_return (0, "too big path-mtu value, maximum is 1450");
533
534   if (path_mtu < 68)
535     return clib_error_return (0, "too small path-mtu value, minimum is 68");
536
537   /* Reset report streams if we are reconfiguring IP addresses */
538   if (frm->ipfix_collector.as_u32 != collector.as_u32 ||
539       frm->src_address.as_u32 != src.as_u32 ||
540       frm->collector_port != collector_port)
541     vnet_flow_reports_reset (frm);
542
543   frm->ipfix_collector.as_u32 = collector.as_u32;
544   frm->collector_port = collector_port;
545   frm->src_address.as_u32 = src.as_u32;
546   frm->fib_index = fib_index;
547   frm->path_mtu = path_mtu;
548   frm->template_interval = template_interval;
549   frm->udp_checksum = udp_checksum;
550
551   if (collector.as_u32)
552     vlib_cli_output (vm, "Collector %U, src address %U, "
553                      "fib index %d, path MTU %u, "
554                      "template resend interval %us, "
555                      "udp checksum %s",
556                      format_ip4_address, &frm->ipfix_collector,
557                      format_ip4_address, &frm->src_address,
558                      fib_index, path_mtu, template_interval,
559                      udp_checksum ? "enabled" : "disabled");
560   else
561     vlib_cli_output (vm, "IPFIX Collector is disabled");
562
563   /* Turn on the flow reporting process */
564   vlib_process_signal_event (vm, flow_report_process_node.index, 1, 0);
565   return 0;
566 }
567
568 /* *INDENT-OFF* */
569 VLIB_CLI_COMMAND (set_ipfix_exporter_command, static) = {
570     .path = "set ipfix exporter",
571     .short_help = "set ipfix exporter "
572                   "collector <ip4-address> [port <port>] "
573                   "src <ip4-address> [fib-id <fib-id>] "
574                   "[path-mtu <path-mtu>] "
575                   "[template-interval <template-interval>]",
576                   "[udp-checksum]",
577     .function = set_ipfix_exporter_command_fn,
578 };
579 /* *INDENT-ON* */
580
581
582 static clib_error_t *
583 ipfix_flush_command_fn (vlib_main_t * vm,
584                         unformat_input_t * input, vlib_cli_command_t * cmd)
585 {
586   /* poke the flow reporting process */
587   vlib_process_signal_event (vm, flow_report_process_node.index, 1, 0);
588   return 0;
589 }
590
591 /* *INDENT-OFF* */
592 VLIB_CLI_COMMAND (ipfix_flush_command, static) = {
593     .path = "ipfix flush",
594     .short_help = "flush the current ipfix data [for make test]",
595     .function = ipfix_flush_command_fn,
596 };
597 /* *INDENT-ON* */
598
599 static clib_error_t *
600 flow_report_init (vlib_main_t * vm)
601 {
602   flow_report_main_t *frm = &flow_report_main;
603
604   frm->vlib_main = vm;
605   frm->vnet_main = vnet_get_main ();
606   frm->unix_time_0 = time (0);
607   frm->vlib_time_0 = vlib_time_now (frm->vlib_main);
608   frm->fib_index = ~0;
609
610   return 0;
611 }
612
613 VLIB_INIT_FUNCTION (flow_report_init)
614 /*
615  * fd.io coding-style-patch-verification: ON
616  *
617  * Local Variables:
618  * eval: (c-set-style "gnu")
619  * End:
620  */