Repair vlib API socket server
[vpp.git] / src / plugins / flowprobe / flowprobe.c
1 /*
2  * flowprobe.c - ipfix probe plugin
3  *
4  * Copyright (c) 2016 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 /**
19  * @file
20  * @brief Per-packet IPFIX flow record generator plugin
21  *
22  * This file implements vpp plugin registration mechanics,
23  * debug CLI, and binary API handling.
24  */
25
26 #include <vnet/vnet.h>
27 #include <vpp/app/version.h>
28 #include <vnet/plugin/plugin.h>
29 #include <flowprobe/flowprobe.h>
30
31 #include <vlibapi/api.h>
32 #include <vlibmemory/api.h>
33
34 /* define message IDs */
35 #include <flowprobe/flowprobe_msg_enum.h>
36
37 /* define message structures */
38 #define vl_typedefs
39 #include <flowprobe/flowprobe_all_api_h.h>
40 #undef vl_typedefs
41
42 /* define generated endian-swappers */
43 #define vl_endianfun
44 #include <flowprobe/flowprobe_all_api_h.h>
45 #undef vl_endianfun
46
47 /* instantiate all the print functions we know about */
48 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
49 #define vl_printfun
50 #include <flowprobe/flowprobe_all_api_h.h>
51 #undef vl_printfun
52
53 flowprobe_main_t flowprobe_main;
54 vlib_node_registration_t flowprobe_walker_node;
55 static vlib_node_registration_t flowprobe_timer_node;
56 uword flowprobe_walker_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
57                                 vlib_frame_t * f);
58
59 /* Get the API version number */
60 #define vl_api_version(n,v) static u32 api_version=(v);
61 #include <flowprobe/flowprobe_all_api_h.h>
62 #undef vl_api_version
63
64 #define REPLY_MSG_ID_BASE fm->msg_id_base
65 #include <vlibapi/api_helper_macros.h>
66
67 /* Define the per-interface configurable features */
68 /* *INDENT-OFF* */
69 VNET_FEATURE_INIT (flow_perpacket_ip4, static) =
70 {
71   .arc_name = "ip4-output",
72   .node_name = "flowprobe-ip4",
73   .runs_before = VNET_FEATURES ("interface-output"),
74 };
75
76 VNET_FEATURE_INIT (flow_perpacket_ip6, static) =
77 {
78   .arc_name = "ip6-output",
79   .node_name = "flowprobe-ip6",
80   .runs_before = VNET_FEATURES ("interface-output"),
81 };
82
83 VNET_FEATURE_INIT (flow_perpacket_l2, static) =
84 {
85   .arc_name = "interface-output",
86   .node_name = "flowprobe-l2",
87   .runs_before = VNET_FEATURES ("interface-tx"),
88 };
89 /* *INDENT-ON* */
90
91 /* Macro to finish up custom dump fns */
92 #define FINISH                                  \
93     vec_add1 (s, 0);                            \
94     vl_print (handle, (char *)s);               \
95     vec_free (s);                               \
96     return handle;
97
98 static inline ipfix_field_specifier_t *
99 flowprobe_template_ip4_fields (ipfix_field_specifier_t * f)
100 {
101 #define flowprobe_template_ip4_field_count() 4
102   /* sourceIpv4Address, TLV type 8, u32 */
103   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
104                                       sourceIPv4Address, 4);
105   f++;
106   /* destinationIPv4Address, TLV type 12, u32 */
107   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
108                                       destinationIPv4Address, 4);
109   f++;
110   /* protocolIdentifier, TLV type 4, u8 */
111   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
112                                       protocolIdentifier, 1);
113   f++;
114   /* octetDeltaCount, TLV type 1, u64 */
115   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
116                                       octetDeltaCount, 8);
117   f++;
118   return f;
119 }
120
121 static inline ipfix_field_specifier_t *
122 flowprobe_template_ip6_fields (ipfix_field_specifier_t * f)
123 {
124 #define flowprobe_template_ip6_field_count() 4
125   /* sourceIpv6Address, TLV type 27, 16 octets */
126   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
127                                       sourceIPv6Address, 16);
128   f++;
129   /* destinationIPv6Address, TLV type 28, 16 octets */
130   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
131                                       destinationIPv6Address, 16);
132   f++;
133   /* protocolIdentifier, TLV type 4, u8 */
134   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
135                                       protocolIdentifier, 1);
136   f++;
137   /* octetDeltaCount, TLV type 1, u64 */
138   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
139                                       octetDeltaCount, 8);
140   f++;
141   return f;
142 }
143
144 static inline ipfix_field_specifier_t *
145 flowprobe_template_l2_fields (ipfix_field_specifier_t * f)
146 {
147 #define flowprobe_template_l2_field_count() 3
148   /* sourceMacAddress, TLV type 56, u8[6] we hope */
149   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
150                                       sourceMacAddress, 6);
151   f++;
152   /* destinationMacAddress, TLV type 80, u8[6] we hope */
153   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
154                                       destinationMacAddress, 6);
155   f++;
156   /* ethernetType, TLV type 256, u16 */
157   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
158                                       ethernetType, 2);
159   f++;
160   return f;
161 }
162
163 static inline ipfix_field_specifier_t *
164 flowprobe_template_common_fields (ipfix_field_specifier_t * f)
165 {
166 #define flowprobe_template_common_field_count() 5
167   /* ingressInterface, TLV type 10, u32 */
168   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
169                                       ingressInterface, 4);
170   f++;
171
172   /* egressInterface, TLV type 14, u32 */
173   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
174                                       egressInterface, 4);
175   f++;
176
177   /* packetDeltaCount, TLV type 2, u64 */
178   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
179                                       packetDeltaCount, 8);
180   f++;
181
182   /* flowStartNanoseconds, TLV type 156, u64 */
183   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
184                                       flowStartNanoseconds, 8);
185   f++;
186
187   /* flowEndNanoseconds, TLV type 157, u64 */
188   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
189                                       flowEndNanoseconds, 8);
190   f++;
191
192   return f;
193 }
194
195 static inline ipfix_field_specifier_t *
196 flowprobe_template_l4_fields (ipfix_field_specifier_t * f)
197 {
198 #define flowprobe_template_l4_field_count() 3
199   /* sourceTransportPort, TLV type 7, u16 */
200   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
201                                       sourceTransportPort, 2);
202   f++;
203   /* destinationTransportPort, TLV type 11, u16 */
204   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
205                                       destinationTransportPort, 2);
206   f++;
207   /* tcpControlBits, TLV type 6, u16 */
208   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
209                                       tcpControlBits, 2);
210   f++;
211
212   return f;
213 }
214
215 /**
216  * @brief Create an IPFIX template packet rewrite string
217  * @param frm flow_report_main_t *
218  * @param fr flow_report_t *
219  * @param collector_address ip4_address_t * the IPFIX collector address
220  * @param src_address ip4_address_t * the source address we should use
221  * @param collector_port u16 the collector port we should use, host byte order
222  * @returns u8 * vector containing the indicated IPFIX template packet
223  */
224 static inline u8 *
225 flowprobe_template_rewrite_inline (flow_report_main_t * frm,
226                                    flow_report_t * fr,
227                                    ip4_address_t * collector_address,
228                                    ip4_address_t * src_address,
229                                    u16 collector_port,
230                                    flowprobe_variant_t which)
231 {
232   ip4_header_t *ip;
233   udp_header_t *udp;
234   ipfix_message_header_t *h;
235   ipfix_set_header_t *s;
236   ipfix_template_header_t *t;
237   ipfix_field_specifier_t *f;
238   ipfix_field_specifier_t *first_field;
239   u8 *rewrite = 0;
240   ip4_ipfix_template_packet_t *tp;
241   u32 field_count = 0;
242   flow_report_stream_t *stream;
243   flowprobe_main_t *fm = &flowprobe_main;
244   flowprobe_record_t flags = fr->opaque.as_uword;
245   bool collect_ip4 = false, collect_ip6 = false;
246
247   stream = &frm->streams[fr->stream_index];
248
249   if (flags & FLOW_RECORD_L3)
250     {
251       collect_ip4 = which == FLOW_VARIANT_L2_IP4 || which == FLOW_VARIANT_IP4;
252       collect_ip6 = which == FLOW_VARIANT_L2_IP6 || which == FLOW_VARIANT_IP6;
253       if (which == FLOW_VARIANT_L2_IP4)
254         flags |= FLOW_RECORD_L2_IP4;
255       if (which == FLOW_VARIANT_L2_IP6)
256         flags |= FLOW_RECORD_L2_IP6;
257     }
258
259   field_count += flowprobe_template_common_field_count ();
260   if (flags & FLOW_RECORD_L2)
261     field_count += flowprobe_template_l2_field_count ();
262   if (collect_ip4)
263     field_count += flowprobe_template_ip4_field_count ();
264   if (collect_ip6)
265     field_count += flowprobe_template_ip6_field_count ();
266   if (flags & FLOW_RECORD_L4)
267     field_count += flowprobe_template_l4_field_count ();
268
269   /* allocate rewrite space */
270   vec_validate_aligned
271     (rewrite, sizeof (ip4_ipfix_template_packet_t)
272      + field_count * sizeof (ipfix_field_specifier_t) - 1,
273      CLIB_CACHE_LINE_BYTES);
274
275   tp = (ip4_ipfix_template_packet_t *) rewrite;
276   ip = (ip4_header_t *) & tp->ip4;
277   udp = (udp_header_t *) (ip + 1);
278   h = (ipfix_message_header_t *) (udp + 1);
279   s = (ipfix_set_header_t *) (h + 1);
280   t = (ipfix_template_header_t *) (s + 1);
281   first_field = f = (ipfix_field_specifier_t *) (t + 1);
282
283   ip->ip_version_and_header_length = 0x45;
284   ip->ttl = 254;
285   ip->protocol = IP_PROTOCOL_UDP;
286   ip->src_address.as_u32 = src_address->as_u32;
287   ip->dst_address.as_u32 = collector_address->as_u32;
288   udp->src_port = clib_host_to_net_u16 (stream->src_port);
289   udp->dst_port = clib_host_to_net_u16 (collector_port);
290   udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip));
291
292   /* FIXUP: message header export_time */
293   /* FIXUP: message header sequence_number */
294   h->domain_id = clib_host_to_net_u32 (stream->domain_id);
295
296   /* Add TLVs to the template */
297   f = flowprobe_template_common_fields (f);
298
299   if (flags & FLOW_RECORD_L2)
300     f = flowprobe_template_l2_fields (f);
301   if (collect_ip4)
302     f = flowprobe_template_ip4_fields (f);
303   if (collect_ip6)
304     f = flowprobe_template_ip6_fields (f);
305   if (flags & FLOW_RECORD_L4)
306     f = flowprobe_template_l4_fields (f);
307
308   /* Back to the template packet... */
309   ip = (ip4_header_t *) & tp->ip4;
310   udp = (udp_header_t *) (ip + 1);
311
312   ASSERT (f - first_field);
313   /* Field count in this template */
314   t->id_count = ipfix_id_count (fr->template_id, f - first_field);
315
316   fm->template_size[flags] = (u8 *) f - (u8 *) s;
317
318   /* set length in octets */
319   s->set_id_length =
320     ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
321
322   /* message length in octets */
323   h->version_length = version_length ((u8 *) f - (u8 *) h);
324
325   ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip);
326   ip->checksum = ip4_header_checksum (ip);
327
328   return rewrite;
329 }
330
331 static u8 *
332 flowprobe_template_rewrite_ip6 (flow_report_main_t * frm,
333                                 flow_report_t * fr,
334                                 ip4_address_t * collector_address,
335                                 ip4_address_t * src_address,
336                                 u16 collector_port)
337 {
338   return flowprobe_template_rewrite_inline
339     (frm, fr, collector_address, src_address, collector_port,
340      FLOW_VARIANT_IP6);
341 }
342
343 static u8 *
344 flowprobe_template_rewrite_ip4 (flow_report_main_t * frm,
345                                 flow_report_t * fr,
346                                 ip4_address_t * collector_address,
347                                 ip4_address_t * src_address,
348                                 u16 collector_port)
349 {
350   return flowprobe_template_rewrite_inline
351     (frm, fr, collector_address, src_address, collector_port,
352      FLOW_VARIANT_IP4);
353 }
354
355 static u8 *
356 flowprobe_template_rewrite_l2 (flow_report_main_t * frm,
357                                flow_report_t * fr,
358                                ip4_address_t * collector_address,
359                                ip4_address_t * src_address,
360                                u16 collector_port)
361 {
362   return flowprobe_template_rewrite_inline
363     (frm, fr, collector_address, src_address, collector_port,
364      FLOW_VARIANT_L2);
365 }
366
367 static u8 *
368 flowprobe_template_rewrite_l2_ip4 (flow_report_main_t * frm,
369                                    flow_report_t * fr,
370                                    ip4_address_t * collector_address,
371                                    ip4_address_t * src_address,
372                                    u16 collector_port)
373 {
374   return flowprobe_template_rewrite_inline
375     (frm, fr, collector_address, src_address, collector_port,
376      FLOW_VARIANT_L2_IP4);
377 }
378
379 static u8 *
380 flowprobe_template_rewrite_l2_ip6 (flow_report_main_t * frm,
381                                    flow_report_t * fr,
382                                    ip4_address_t * collector_address,
383                                    ip4_address_t * src_address,
384                                    u16 collector_port)
385 {
386   return flowprobe_template_rewrite_inline
387     (frm, fr, collector_address, src_address, collector_port,
388      FLOW_VARIANT_L2_IP6);
389 }
390
391 /**
392  * @brief Flush accumulated data
393  * @param frm flow_report_main_t *
394  * @param fr flow_report_t *
395  * @param f vlib_frame_t *
396  *
397  * <em>Notes:</em>
398  * This function must simply return the incoming frame, or no template packets
399  * will be sent.
400  */
401 vlib_frame_t *
402 flowprobe_data_callback_ip4 (flow_report_main_t * frm,
403                              flow_report_t * fr,
404                              vlib_frame_t * f, u32 * to_next, u32 node_index)
405 {
406   flowprobe_flush_callback_ip4 ();
407   return f;
408 }
409
410 vlib_frame_t *
411 flowprobe_data_callback_ip6 (flow_report_main_t * frm,
412                              flow_report_t * fr,
413                              vlib_frame_t * f, u32 * to_next, u32 node_index)
414 {
415   flowprobe_flush_callback_ip6 ();
416   return f;
417 }
418
419 vlib_frame_t *
420 flowprobe_data_callback_l2 (flow_report_main_t * frm,
421                             flow_report_t * fr,
422                             vlib_frame_t * f, u32 * to_next, u32 node_index)
423 {
424   flowprobe_flush_callback_l2 ();
425   return f;
426 }
427
428 static int
429 flowprobe_template_add_del (u32 domain_id, u16 src_port,
430                             flowprobe_record_t flags,
431                             vnet_flow_data_callback_t * flow_data_callback,
432                             vnet_flow_rewrite_callback_t * rewrite_callback,
433                             bool is_add, u16 * template_id)
434 {
435   flow_report_main_t *frm = &flow_report_main;
436   vnet_flow_report_add_del_args_t a = {
437     .rewrite_callback = rewrite_callback,
438     .flow_data_callback = flow_data_callback,
439     .is_add = is_add,
440     .domain_id = domain_id,
441     .src_port = src_port,
442     .opaque.as_uword = flags,
443   };
444   return vnet_flow_report_add_del (frm, &a, template_id);
445 }
446
447 static void
448 flowprobe_expired_timer_callback (u32 * expired_timers)
449 {
450   vlib_main_t *vm = vlib_get_main ();
451   flowprobe_main_t *fm = &flowprobe_main;
452   u32 my_cpu_number = vm->thread_index;
453   int i;
454   u32 poolindex;
455
456   for (i = 0; i < vec_len (expired_timers); i++)
457     {
458       poolindex = expired_timers[i] & 0x7FFFFFFF;
459       vec_add1 (fm->expired_passive_per_worker[my_cpu_number], poolindex);
460     }
461 }
462
463 static clib_error_t *
464 flowprobe_create_state_tables (u32 active_timer)
465 {
466   flowprobe_main_t *fm = &flowprobe_main;
467   vlib_thread_main_t *tm = &vlib_thread_main;
468   vlib_main_t *vm = vlib_get_main ();
469   clib_error_t *error = 0;
470   u32 num_threads;
471   int i;
472
473   /* Decide how many worker threads we have */
474   num_threads = 1 /* main thread */  + tm->n_threads;
475
476   /* Hash table per worker */
477   fm->ht_log2len = FLOWPROBE_LOG2_HASHSIZE;
478
479   /* Init per worker flow state and timer wheels */
480   if (active_timer)
481     {
482       vec_validate (fm->timers_per_worker, num_threads - 1);
483       vec_validate (fm->expired_passive_per_worker, num_threads - 1);
484       vec_validate (fm->hash_per_worker, num_threads - 1);
485       vec_validate (fm->pool_per_worker, num_threads - 1);
486
487       for (i = 0; i < num_threads; i++)
488         {
489           int j;
490           pool_alloc (fm->pool_per_worker[i], 1 << fm->ht_log2len);
491           vec_resize (fm->hash_per_worker[i], 1 << fm->ht_log2len);
492           for (j = 0; j < (1 << fm->ht_log2len); j++)
493             fm->hash_per_worker[i][j] = ~0;
494           fm->timers_per_worker[i] =
495             clib_mem_alloc (sizeof (TWT (tw_timer_wheel)));
496           tw_timer_wheel_init_2t_1w_2048sl (fm->timers_per_worker[i],
497                                             flowprobe_expired_timer_callback,
498                                             1.0, 1024);
499         }
500       fm->disabled = true;
501     }
502   else
503     {
504       f64 now = vlib_time_now (vm);
505       vec_validate (fm->stateless_entry, num_threads - 1);
506       for (i = 0; i < num_threads; i++)
507         fm->stateless_entry[i].last_exported = now;
508       fm->disabled = false;
509     }
510   fm->initialized = true;
511   return error;
512 }
513
514 static int
515 validate_feature_on_interface (flowprobe_main_t * fm, u32 sw_if_index,
516                                u8 which)
517 {
518   vec_validate_init_empty (fm->flow_per_interface, sw_if_index, ~0);
519
520   if (fm->flow_per_interface[sw_if_index] == (u8) ~ 0)
521     return -1;
522   else if (fm->flow_per_interface[sw_if_index] != which)
523     return 0;
524   else
525     return 1;
526 }
527
528 /**
529  * @brief configure / deconfigure the IPFIX flow-per-packet
530  * @param fm flowprobe_main_t * fm
531  * @param sw_if_index u32 the desired interface
532  * @param is_add int 1 to enable the feature, 0 to disable it
533  * @returns 0 if successful, non-zero otherwise
534  */
535
536 static int
537 flowprobe_tx_interface_add_del_feature (flowprobe_main_t * fm,
538                                         u32 sw_if_index, u8 which, int is_add)
539 {
540   vlib_main_t *vm = vlib_get_main ();
541   int rv = 0;
542   u16 template_id = 0;
543   flowprobe_record_t flags = fm->record;
544
545   fm->flow_per_interface[sw_if_index] = (is_add) ? which : (u8) ~ 0;
546   fm->template_per_flow[which] += (is_add) ? 1 : -1;
547   if (is_add && fm->template_per_flow[which] > 1)
548     template_id = fm->template_reports[flags];
549
550   if ((is_add && fm->template_per_flow[which] == 1) ||
551       (!is_add && fm->template_per_flow[which] == 0))
552     {
553       if (which == FLOW_VARIANT_L2)
554         {
555           if (fm->record & FLOW_RECORD_L2)
556             {
557               rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags,
558                                                flowprobe_data_callback_l2,
559                                                flowprobe_template_rewrite_l2,
560                                                is_add, &template_id);
561             }
562           if (fm->record & FLOW_RECORD_L3 || fm->record & FLOW_RECORD_L4)
563             {
564               rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags,
565                                                flowprobe_data_callback_l2,
566                                                flowprobe_template_rewrite_l2_ip4,
567                                                is_add, &template_id);
568               fm->template_reports[flags | FLOW_RECORD_L2_IP4] =
569                 (is_add) ? template_id : 0;
570               rv =
571                 flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags,
572                                             flowprobe_data_callback_l2,
573                                             flowprobe_template_rewrite_l2_ip6,
574                                             is_add, &template_id);
575               fm->template_reports[flags | FLOW_RECORD_L2_IP6] =
576                 (is_add) ? template_id : 0;
577
578               /* Special case L2 */
579               fm->context[FLOW_VARIANT_L2_IP4].flags =
580                 flags | FLOW_RECORD_L2_IP4;
581               fm->context[FLOW_VARIANT_L2_IP6].flags =
582                 flags | FLOW_RECORD_L2_IP6;
583
584               fm->template_reports[flags] = template_id;
585             }
586         }
587       else if (which == FLOW_VARIANT_IP4)
588         rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags,
589                                          flowprobe_data_callback_ip4,
590                                          flowprobe_template_rewrite_ip4,
591                                          is_add, &template_id);
592       else if (which == FLOW_VARIANT_IP6)
593         rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags,
594                                          flowprobe_data_callback_ip6,
595                                          flowprobe_template_rewrite_ip6,
596                                          is_add, &template_id);
597     }
598   if (rv && rv != VNET_API_ERROR_VALUE_EXIST)
599     {
600       clib_warning ("vnet_flow_report_add_del returned %d", rv);
601       return -1;
602     }
603
604   if (which != (u8) ~ 0)
605     {
606       fm->context[which].flags = fm->record;
607       fm->template_reports[flags] = (is_add) ? template_id : 0;
608     }
609
610   if (which == FLOW_VARIANT_IP4)
611     vnet_feature_enable_disable ("ip4-output", "flowprobe-ip4",
612                                  sw_if_index, is_add, 0, 0);
613   else if (which == FLOW_VARIANT_IP6)
614     vnet_feature_enable_disable ("ip6-output", "flowprobe-ip6",
615                                  sw_if_index, is_add, 0, 0);
616   else if (which == FLOW_VARIANT_L2)
617     vnet_feature_enable_disable ("interface-output", "flowprobe-l2",
618                                  sw_if_index, is_add, 0, 0);
619
620   /* Stateful flow collection */
621   if (is_add && !fm->initialized)
622     {
623       flowprobe_create_state_tables (fm->active_timer);
624       if (fm->active_timer)
625         vlib_process_signal_event (vm, flowprobe_timer_node.index, 1, 0);
626     }
627
628   return 0;
629 }
630
631 /**
632  * @brief API message handler
633  * @param mp vl_api_flowprobe_tx_interface_add_del_t * mp the api message
634  */
635 void vl_api_flowprobe_tx_interface_add_del_t_handler
636   (vl_api_flowprobe_tx_interface_add_del_t * mp)
637 {
638   flowprobe_main_t *fm = &flowprobe_main;
639   vl_api_flowprobe_tx_interface_add_del_reply_t *rmp;
640   u32 sw_if_index = ntohl (mp->sw_if_index);
641   int rv = 0;
642
643   VALIDATE_SW_IF_INDEX (mp);
644
645   if (mp->which != FLOW_VARIANT_IP4 && mp->which != FLOW_VARIANT_L2
646       && mp->which != FLOW_VARIANT_IP6)
647     {
648       rv = VNET_API_ERROR_UNIMPLEMENTED;
649       goto out;
650     }
651
652   if (fm->record == 0)
653     {
654       clib_warning ("Please specify flowprobe params record first...");
655       rv = VNET_API_ERROR_CANNOT_ENABLE_DISABLE_FEATURE;
656       goto out;
657     }
658
659   rv = validate_feature_on_interface (fm, sw_if_index, mp->which);
660   if ((rv == 1 && mp->is_add == 1) || rv == 0)
661     {
662       rv = VNET_API_ERROR_CANNOT_ENABLE_DISABLE_FEATURE;
663       goto out;
664     }
665
666   rv = flowprobe_tx_interface_add_del_feature
667     (fm, sw_if_index, mp->which, mp->is_add);
668
669 out:
670   BAD_SW_IF_INDEX_LABEL;
671
672   REPLY_MACRO (VL_API_FLOWPROBE_TX_INTERFACE_ADD_DEL_REPLY);
673 }
674
675 /**
676  * @brief API message custom-dump function
677  * @param mp vl_api_flowprobe_tx_interface_add_del_t * mp the api message
678  * @param handle void * print function handle
679  * @returns u8 * output string
680  */
681 static void *vl_api_flowprobe_tx_interface_add_del_t_print
682   (vl_api_flowprobe_tx_interface_add_del_t * mp, void *handle)
683 {
684   u8 *s;
685
686   s = format (0, "SCRIPT: flowprobe_tx_interface_add_del ");
687   s = format (s, "sw_if_index %d is_add %d which %d ",
688               clib_host_to_net_u32 (mp->sw_if_index),
689               (int) mp->is_add, (int) mp->which);
690   FINISH;
691 }
692
693 #define vec_neg_search(v,E)         \
694 ({              \
695   word _v(i) = 0;         \
696   while (_v(i) < vec_len(v) && v[_v(i)] == E)        \
697   {             \
698     _v(i)++;            \
699   }             \
700   if (_v(i) == vec_len(v))        \
701     _v(i) = ~0;                 \
702   _v(i);            \
703 })
704
705 static int
706 flowprobe_params (flowprobe_main_t * fm, u8 record_l2,
707                   u8 record_l3, u8 record_l4,
708                   u32 active_timer, u32 passive_timer)
709 {
710   flowprobe_record_t flags = 0;
711
712   if (vec_neg_search (fm->flow_per_interface, (u8) ~ 0) != ~0)
713     return ~0;
714
715   if (record_l2)
716     flags |= FLOW_RECORD_L2;
717   if (record_l3)
718     flags |= FLOW_RECORD_L3;
719   if (record_l4)
720     flags |= FLOW_RECORD_L4;
721
722   fm->record = flags;
723
724   /*
725    * Timers: ~0 is default, 0 is off
726    */
727   fm->active_timer =
728     (active_timer == (u32) ~ 0 ? FLOWPROBE_TIMER_ACTIVE : active_timer);
729   fm->passive_timer =
730     (passive_timer == (u32) ~ 0 ? FLOWPROBE_TIMER_PASSIVE : passive_timer);
731
732   return 0;
733 }
734
735 void
736 vl_api_flowprobe_params_t_handler (vl_api_flowprobe_params_t * mp)
737 {
738   flowprobe_main_t *fm = &flowprobe_main;
739   vl_api_flowprobe_params_reply_t *rmp;
740   int rv = 0;
741
742   rv = flowprobe_params
743     (fm, mp->record_l2, mp->record_l3, mp->record_l4,
744      clib_net_to_host_u32 (mp->active_timer),
745      clib_net_to_host_u32 (mp->passive_timer));
746
747   REPLY_MACRO (VL_API_FLOWPROBE_PARAMS_REPLY);
748 }
749
750 /* List of message types that this plugin understands */
751 #define foreach_flowprobe_plugin_api_msg                                \
752 _(FLOWPROBE_TX_INTERFACE_ADD_DEL, flowprobe_tx_interface_add_del)       \
753 _(FLOWPROBE_PARAMS, flowprobe_params)
754
755 /* *INDENT-OFF* */
756 VLIB_PLUGIN_REGISTER () = {
757     .version = VPP_BUILD_VER,
758     .description = "Flow per Packet",
759 };
760 /* *INDENT-ON* */
761
762 u8 *
763 format_flowprobe_entry (u8 * s, va_list * args)
764 {
765   flowprobe_entry_t *e = va_arg (*args, flowprobe_entry_t *);
766   s = format (s, " %d/%d", e->key.rx_sw_if_index, e->key.tx_sw_if_index);
767
768   s = format (s, " %U %U", format_ethernet_address, &e->key.src_mac,
769               format_ethernet_address, &e->key.dst_mac);
770   s = format (s, " %U -> %U",
771               format_ip46_address, &e->key.src_address, IP46_TYPE_ANY,
772               format_ip46_address, &e->key.dst_address, IP46_TYPE_ANY);
773   s = format (s, " %d", e->key.protocol);
774   s = format (s, " %d %d\n", clib_net_to_host_u16 (e->key.src_port),
775               clib_net_to_host_u16 (e->key.dst_port));
776
777   return s;
778 }
779
780 static clib_error_t *
781 flowprobe_show_table_fn (vlib_main_t * vm,
782                          unformat_input_t * input, vlib_cli_command_t * cm)
783 {
784   flowprobe_main_t *fm = &flowprobe_main;
785   int i;
786   flowprobe_entry_t *e;
787
788   vlib_cli_output (vm, "Dumping IPFIX table");
789
790   for (i = 0; i < vec_len (fm->pool_per_worker); i++)
791     {
792       /* *INDENT-OFF* */
793       pool_foreach (e, fm->pool_per_worker[i], (
794         {
795           vlib_cli_output (vm, "%U",
796                            format_flowprobe_entry,
797                            e);
798         }));
799       /* *INDENT-ON* */
800
801     }
802   return 0;
803 }
804
805 static clib_error_t *
806 flowprobe_show_stats_fn (vlib_main_t * vm,
807                          unformat_input_t * input, vlib_cli_command_t * cm)
808 {
809   flowprobe_main_t *fm = &flowprobe_main;
810   int i;
811
812   vlib_cli_output (vm, "IPFIX table statistics");
813   vlib_cli_output (vm, "Flow entry size: %d\n", sizeof (flowprobe_entry_t));
814   vlib_cli_output (vm, "Flow pool size per thread: %d\n",
815                    0x1 << FLOWPROBE_LOG2_HASHSIZE);
816
817   for (i = 0; i < vec_len (fm->pool_per_worker); i++)
818     vlib_cli_output (vm, "Pool utilisation thread %d is %d%%\n", i,
819                      (100 * pool_elts (fm->pool_per_worker[i])) /
820                      (0x1 << FLOWPROBE_LOG2_HASHSIZE));
821   return 0;
822 }
823
824 static clib_error_t *
825 flowprobe_tx_interface_add_del_feature_command_fn (vlib_main_t * vm,
826                                                    unformat_input_t * input,
827                                                    vlib_cli_command_t * cmd)
828 {
829   flowprobe_main_t *fm = &flowprobe_main;
830   u32 sw_if_index = ~0;
831   int is_add = 1;
832   u8 which = FLOW_VARIANT_IP4;
833   int rv;
834
835   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
836     {
837       if (unformat (input, "disable"))
838         is_add = 0;
839       else if (unformat (input, "%U", unformat_vnet_sw_interface,
840                          fm->vnet_main, &sw_if_index));
841       else if (unformat (input, "ip4"))
842         which = FLOW_VARIANT_IP4;
843       else if (unformat (input, "ip6"))
844         which = FLOW_VARIANT_IP6;
845       else if (unformat (input, "l2"))
846         which = FLOW_VARIANT_L2;
847       else
848         break;
849     }
850
851   if (fm->record == 0)
852     return clib_error_return (0,
853                               "Please specify flowprobe params record first...");
854
855   if (sw_if_index == ~0)
856     return clib_error_return (0, "Please specify an interface...");
857
858   rv = validate_feature_on_interface (fm, sw_if_index, which);
859   if (rv == 1)
860     {
861       if (is_add)
862         return clib_error_return (0,
863                                   "Datapath is already enabled for given interface...");
864     }
865   else if (rv == 0)
866     return clib_error_return (0,
867                               "Interface has enable different datapath ...");
868
869   rv =
870     flowprobe_tx_interface_add_del_feature (fm, sw_if_index, which, is_add);
871   switch (rv)
872     {
873     case 0:
874       break;
875
876     case VNET_API_ERROR_INVALID_SW_IF_INDEX:
877       return clib_error_return
878         (0, "Invalid interface, only works on physical ports");
879       break;
880
881     case VNET_API_ERROR_UNIMPLEMENTED:
882       return clib_error_return (0, "ip6 not supported");
883       break;
884
885     default:
886       return clib_error_return (0, "flowprobe_enable_disable returned %d",
887                                 rv);
888     }
889   return 0;
890 }
891
892 static clib_error_t *
893 flowprobe_params_command_fn (vlib_main_t * vm,
894                              unformat_input_t * input,
895                              vlib_cli_command_t * cmd)
896 {
897   flowprobe_main_t *fm = &flowprobe_main;
898   bool record_l2 = false, record_l3 = false, record_l4 = false;
899   u32 active_timer = ~0;
900   u32 passive_timer = ~0;
901
902   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
903     {
904       if (unformat (input, "active %d", &active_timer))
905         ;
906       else if (unformat (input, "passive %d", &passive_timer))
907         ;
908       else if (unformat (input, "record"))
909         while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
910           {
911             if (unformat (input, "l2"))
912               record_l2 = true;
913             else if (unformat (input, "l3"))
914               record_l3 = true;
915             else if (unformat (input, "l4"))
916               record_l4 = true;
917             else
918               break;
919           }
920       else
921         break;
922     }
923
924   if (passive_timer > 0 && active_timer > passive_timer)
925     return clib_error_return (0,
926                               "Passive timer has to be greater than active one...");
927
928   if (flowprobe_params (fm, record_l2, record_l3, record_l4,
929                         active_timer, passive_timer))
930     return clib_error_return (0,
931                               "Couldn't change flowperpacket params when feature is enabled on some interface ...");
932   return 0;
933 }
934
935 /*?
936  * '<em>flowprobe feature add-del</em>' commands to enable/disable
937  * per-packet IPFIX flow record generation on an interface
938  *
939  * @cliexpar
940  * @parblock
941  * To enable per-packet IPFIX flow-record generation on an interface:
942  * @cliexcmd{flowprobe feature add-del GigabitEthernet2/0/0}
943  *
944  * To disable per-packet IPFIX flow-record generation on an interface:
945  * @cliexcmd{flowprobe feature add-del GigabitEthernet2/0/0 disable}
946  * @cliexend
947  * @endparblock
948 ?*/
949 /* *INDENT-OFF* */
950 VLIB_CLI_COMMAND (flowprobe_enable_disable_command, static) = {
951     .path = "flowprobe feature add-del",
952     .short_help =
953     "flowprobe feature add-del <interface-name> <l2|ip4|ip6> disable",
954     .function = flowprobe_tx_interface_add_del_feature_command_fn,
955 };
956 VLIB_CLI_COMMAND (flowprobe_params_command, static) = {
957     .path = "flowprobe params",
958     .short_help =
959     "flowprobe params record <[l2] [l3] [l4]> [active <timer> passive <timer>]",
960     .function = flowprobe_params_command_fn,
961 };
962 VLIB_CLI_COMMAND (flowprobe_show_table_command, static) = {
963     .path = "show flowprobe table",
964     .short_help = "show flowprobe table",
965     .function = flowprobe_show_table_fn,
966 };
967 VLIB_CLI_COMMAND (flowprobe_show_stats_command, static) = {
968     .path = "show flowprobe statistics",
969     .short_help = "show flowprobe statistics",
970     .function = flowprobe_show_stats_fn,
971 };
972 /* *INDENT-ON* */
973
974 /**
975  * @brief Set up the API message handling tables
976  * @param vm vlib_main_t * vlib main data structure pointer
977  * @returns 0 to indicate all is well
978  */
979 static clib_error_t *
980 flowprobe_plugin_api_hookup (vlib_main_t * vm)
981 {
982   flowprobe_main_t *fm = &flowprobe_main;
983 #define _(N,n)                                                  \
984     vl_msg_api_set_handlers((VL_API_##N + fm->msg_id_base),     \
985                            #n,                                  \
986                            vl_api_##n##_t_handler,              \
987                            vl_noop_handler,                     \
988                            vl_api_##n##_t_endian,               \
989                            vl_api_##n##_t_print,                \
990                            sizeof(vl_api_##n##_t), 1);
991   foreach_flowprobe_plugin_api_msg;
992 #undef _
993
994   return 0;
995 }
996
997 #define vl_msg_name_crc_list
998 #include <flowprobe/flowprobe_all_api_h.h>
999 #undef vl_msg_name_crc_list
1000
1001 static void
1002 setup_message_id_table (flowprobe_main_t * fm, api_main_t * am)
1003 {
1004 #define _(id,n,crc) \
1005   vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + fm->msg_id_base);
1006   foreach_vl_msg_name_crc_flowprobe;
1007 #undef _
1008 }
1009
1010 /*
1011  * Main-core process, sending an interrupt to the per worker input
1012  * process that spins the per worker timer wheel.
1013  */
1014 static uword
1015 timer_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
1016 {
1017   uword *event_data = 0;
1018   vlib_main_t **worker_vms = 0, *worker_vm;
1019   flowprobe_main_t *fm = &flowprobe_main;
1020
1021   /* Wait for Godot... */
1022   vlib_process_wait_for_event_or_clock (vm, 1e9);
1023   uword event_type = vlib_process_get_events (vm, &event_data);
1024   if (event_type != 1)
1025     clib_warning ("bogus kickoff event received, %d", event_type);
1026   vec_reset_length (event_data);
1027
1028   int i;
1029   if (vec_len (vlib_mains) == 0)
1030     vec_add1 (worker_vms, vm);
1031   else
1032     {
1033       for (i = 0; i < vec_len (vlib_mains); i++)
1034         {
1035           worker_vm = vlib_mains[i];
1036           if (worker_vm)
1037             vec_add1 (worker_vms, worker_vm);
1038         }
1039     }
1040   f64 sleep_duration = 0.1;
1041
1042   while (1)
1043     {
1044       /* Send an interrupt to each timer input node */
1045       sleep_duration = 0.1;
1046       for (i = 0; i < vec_len (worker_vms); i++)
1047         {
1048           worker_vm = worker_vms[i];
1049           if (worker_vm)
1050             {
1051               vlib_node_set_interrupt_pending (worker_vm,
1052                                                flowprobe_walker_node.index);
1053               sleep_duration =
1054                 (fm->expired_passive_per_worker[i] > 0) ? 1e-4 : 0.1;
1055             }
1056         }
1057       vlib_process_suspend (vm, sleep_duration);
1058     }
1059   return 0;                     /* or not */
1060 }
1061
1062 /* *INDENT-OFF* */
1063 VLIB_REGISTER_NODE (flowprobe_timer_node,static) = {
1064   .function = timer_process,
1065   .name = "flowprobe-timer-process",
1066   .type = VLIB_NODE_TYPE_PROCESS,
1067 };
1068 /* *INDENT-ON* */
1069
1070 /**
1071  * @brief Set up the API message handling tables
1072  * @param vm vlib_main_t * vlib main data structure pointer
1073  * @returns 0 to indicate all is well, or a clib_error_t
1074  */
1075 static clib_error_t *
1076 flowprobe_init (vlib_main_t * vm)
1077 {
1078   flowprobe_main_t *fm = &flowprobe_main;
1079   vlib_thread_main_t *tm = &vlib_thread_main;
1080   clib_error_t *error = 0;
1081   u8 *name;
1082   u32 num_threads;
1083   int i;
1084
1085   fm->vnet_main = vnet_get_main ();
1086
1087   /* Construct the API name */
1088   name = format (0, "flowprobe_%08x%c", api_version, 0);
1089
1090   /* Ask for a correctly-sized block of API message decode slots */
1091   fm->msg_id_base = vl_msg_api_get_msg_ids
1092     ((char *) name, VL_MSG_FIRST_AVAILABLE);
1093
1094   /* Hook up message handlers */
1095   error = flowprobe_plugin_api_hookup (vm);
1096
1097   /* Add our API messages to the global name_crc hash table */
1098   setup_message_id_table (fm, &api_main);
1099
1100   vec_free (name);
1101
1102   /* Set up time reference pair */
1103   fm->vlib_time_0 = vlib_time_now (vm);
1104   fm->nanosecond_time_0 = unix_time_now_nsec ();
1105
1106   memset (fm->template_reports, 0, sizeof (fm->template_reports));
1107   memset (fm->template_size, 0, sizeof (fm->template_size));
1108   memset (fm->template_per_flow, 0, sizeof (fm->template_per_flow));
1109
1110   /* Decide how many worker threads we have */
1111   num_threads = 1 /* main thread */  + tm->n_threads;
1112
1113   /* Allocate per worker thread vectors per flavour */
1114   for (i = 0; i < FLOW_N_VARIANTS; i++)
1115     {
1116       vec_validate (fm->context[i].buffers_per_worker, num_threads - 1);
1117       vec_validate (fm->context[i].frames_per_worker, num_threads - 1);
1118       vec_validate (fm->context[i].next_record_offset_per_worker,
1119                     num_threads - 1);
1120     }
1121
1122   fm->active_timer = FLOWPROBE_TIMER_ACTIVE;
1123   fm->passive_timer = FLOWPROBE_TIMER_PASSIVE;
1124
1125   return error;
1126 }
1127
1128 VLIB_INIT_FUNCTION (flowprobe_init);
1129
1130 /*
1131  * fd.io coding-style-patch-verification: ON
1132  *
1133  * Local Variables:
1134  * eval: (c-set-style "gnu")
1135  * End:
1136  */