LCOV - code coverage report
Current view: top level - plugins/flowprobe - flowprobe.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 396 598 66.2 %
Date: 2023-07-05 22:20:52 Functions: 59 71 83.1 %

          Line data    Source code
       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 <vnet/udp/udp_local.h>
      30             : #include <flowprobe/flowprobe.h>
      31             : 
      32             : #include <vlibapi/api.h>
      33             : #include <vlibmemory/api.h>
      34             : 
      35             : /* define message IDs */
      36             : #include <flowprobe/flowprobe.api_enum.h>
      37             : #include <flowprobe/flowprobe.api_types.h>
      38             : 
      39             : flowprobe_main_t flowprobe_main;
      40             : static vlib_node_registration_t flowprobe_timer_node;
      41             : uword flowprobe_walker_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
      42             :                                 vlib_frame_t * f);
      43             : 
      44             : #define REPLY_MSG_ID_BASE fm->msg_id_base
      45             : #include <vlibapi/api_helper_macros.h>
      46             : 
      47             : /* Define the per-interface configurable features */
      48             : /* *INDENT-OFF* */
      49       51543 : VNET_FEATURE_INIT (flowprobe_input_ip4_unicast, static) = {
      50             :   .arc_name = "ip4-unicast",
      51             :   .node_name = "flowprobe-input-ip4",
      52             :   .runs_before = VNET_FEATURES ("ip4-lookup"),
      53             : };
      54       51543 : VNET_FEATURE_INIT (flowprobe_input_ip4_multicast, static) = {
      55             :   .arc_name = "ip4-multicast",
      56             :   .node_name = "flowprobe-input-ip4",
      57             :   .runs_before = VNET_FEATURES ("ip4-mfib-forward-lookup"),
      58             : };
      59       51543 : VNET_FEATURE_INIT (flowprobe_input_ip6_unicast, static) = {
      60             :   .arc_name = "ip6-unicast",
      61             :   .node_name = "flowprobe-input-ip6",
      62             :   .runs_before = VNET_FEATURES ("ip6-lookup"),
      63             : };
      64       51543 : VNET_FEATURE_INIT (flowprobe_input_ip6_multicast, static) = {
      65             :   .arc_name = "ip6-multicast",
      66             :   .node_name = "flowprobe-input-ip6",
      67             :   .runs_before = VNET_FEATURES ("ip6-mfib-forward-lookup"),
      68             : };
      69       51543 : VNET_FEATURE_INIT (flowprobe_input_l2, static) = {
      70             :   .arc_name = "device-input",
      71             :   .node_name = "flowprobe-input-l2",
      72             :   .runs_before = VNET_FEATURES ("ethernet-input"),
      73             : };
      74       51543 : VNET_FEATURE_INIT (flowprobe_output_ip4, static) = {
      75             :   .arc_name = "ip4-output",
      76             :   .node_name = "flowprobe-output-ip4",
      77             :   .runs_before = VNET_FEATURES ("interface-output"),
      78             : };
      79             : 
      80       51543 : VNET_FEATURE_INIT (flowprobe_output_ip6, static) = {
      81             :   .arc_name = "ip6-output",
      82             :   .node_name = "flowprobe-output-ip6",
      83             :   .runs_before = VNET_FEATURES ("interface-output"),
      84             : };
      85             : 
      86       51543 : VNET_FEATURE_INIT (flowprobe_output_l2, static) = {
      87             :   .arc_name = "interface-output",
      88             :   .node_name = "flowprobe-output-l2",
      89             :   .runs_before = VNET_FEATURES ("interface-output-arc-end"),
      90             : };
      91             : /* *INDENT-ON* */
      92             : 
      93             : #define FINISH                                                                \
      94             :   vec_add1 (s, 0);                                                            \
      95             :   vlib_cli_output (handle, (char *) s);                                       \
      96             :   vec_free (s);                                                               \
      97             :   return handle;
      98             : 
      99             : static inline ipfix_field_specifier_t *
     100          17 : flowprobe_template_ip4_fields (ipfix_field_specifier_t * f)
     101             : {
     102             : #define flowprobe_template_ip4_field_count() 4
     103             :   /* sourceIpv4Address, TLV type 8, u32 */
     104          17 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     105             :                                       sourceIPv4Address, 4);
     106          17 :   f++;
     107             :   /* destinationIPv4Address, TLV type 12, u32 */
     108          17 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     109             :                                       destinationIPv4Address, 4);
     110          17 :   f++;
     111             :   /* protocolIdentifier, TLV type 4, u8 */
     112          17 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     113             :                                       protocolIdentifier, 1);
     114          17 :   f++;
     115             :   /* octetDeltaCount, TLV type 1, u64 */
     116          17 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     117             :                                       octetDeltaCount, 8);
     118          17 :   f++;
     119          17 :   return f;
     120             : }
     121             : 
     122             : static inline ipfix_field_specifier_t *
     123          16 : flowprobe_template_ip6_fields (ipfix_field_specifier_t * f)
     124             : {
     125             : #define flowprobe_template_ip6_field_count() 4
     126             :   /* sourceIpv6Address, TLV type 27, 16 octets */
     127          16 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     128             :                                       sourceIPv6Address, 16);
     129          16 :   f++;
     130             :   /* destinationIPv6Address, TLV type 28, 16 octets */
     131          16 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     132             :                                       destinationIPv6Address, 16);
     133          16 :   f++;
     134             :   /* protocolIdentifier, TLV type 4, u8 */
     135          16 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     136             :                                       protocolIdentifier, 1);
     137          16 :   f++;
     138             :   /* octetDeltaCount, TLV type 1, u64 */
     139          16 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     140             :                                       octetDeltaCount, 8);
     141          16 :   f++;
     142          16 :   return f;
     143             : }
     144             : 
     145             : static inline ipfix_field_specifier_t *
     146          43 : flowprobe_template_l2_fields (ipfix_field_specifier_t * f)
     147             : {
     148             : #define flowprobe_template_l2_field_count() 3
     149             :   /* sourceMacAddress, TLV type 56, u8[6] we hope */
     150          43 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     151             :                                       sourceMacAddress, 6);
     152          43 :   f++;
     153             :   /* destinationMacAddress, TLV type 80, u8[6] we hope */
     154          43 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     155             :                                       destinationMacAddress, 6);
     156          43 :   f++;
     157             :   /* ethernetType, TLV type 256, u16 */
     158          43 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     159             :                                       ethernetType, 2);
     160          43 :   f++;
     161          43 :   return f;
     162             : }
     163             : 
     164             : static inline ipfix_field_specifier_t *
     165          59 : flowprobe_template_common_fields (ipfix_field_specifier_t * f)
     166             : {
     167             : #define flowprobe_template_common_field_count() 6
     168             :   /* ingressInterface, TLV type 10, u32 */
     169          59 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     170             :                                       ingressInterface, 4);
     171          59 :   f++;
     172             : 
     173             :   /* egressInterface, TLV type 14, u32 */
     174          59 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     175             :                                       egressInterface, 4);
     176          59 :   f++;
     177             : 
     178             :   /* flowDirection, TLV type 61, u8 */
     179          59 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */, flowDirection, 1);
     180          59 :   f++;
     181             : 
     182             :   /* packetDeltaCount, TLV type 2, u64 */
     183          59 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     184             :                                       packetDeltaCount, 8);
     185          59 :   f++;
     186             : 
     187             :   /* flowStartNanoseconds, TLV type 156, u64 */
     188          59 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     189             :                                       flowStartNanoseconds, 8);
     190          59 :   f++;
     191             : 
     192             :   /* flowEndNanoseconds, TLV type 157, u64 */
     193          59 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     194             :                                       flowEndNanoseconds, 8);
     195          59 :   f++;
     196             : 
     197          59 :   return f;
     198             : }
     199             : 
     200             : static inline ipfix_field_specifier_t *
     201          43 : flowprobe_template_l4_fields (ipfix_field_specifier_t * f)
     202             : {
     203             : #define flowprobe_template_l4_field_count() 3
     204             :   /* sourceTransportPort, TLV type 7, u16 */
     205          43 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     206             :                                       sourceTransportPort, 2);
     207          43 :   f++;
     208             :   /* destinationTransportPort, TLV type 11, u16 */
     209          43 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     210             :                                       destinationTransportPort, 2);
     211          43 :   f++;
     212             :   /* tcpControlBits, TLV type 6, u16 */
     213          43 :   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
     214             :                                       tcpControlBits, 2);
     215          43 :   f++;
     216             : 
     217          43 :   return f;
     218             : }
     219             : 
     220             : /**
     221             :  * @brief Create an IPFIX template packet rewrite string
     222             :  * @param frm flow_report_main_t *
     223             :  * @param fr flow_report_t *
     224             :  * @param collector_address ip4_address_t * the IPFIX collector address
     225             :  * @param src_address ip4_address_t * the source address we should use
     226             :  * @param collector_port u16 the collector port we should use, host byte order
     227             :  * @returns u8 * vector containing the indicated IPFIX template packet
     228             :  */
     229             : static inline u8 *
     230          59 : flowprobe_template_rewrite_inline (ipfix_exporter_t *exp, flow_report_t *fr,
     231             :                                    u16 collector_port,
     232             :                                    flowprobe_variant_t which)
     233             : {
     234             :   ip4_header_t *ip;
     235             :   udp_header_t *udp;
     236             :   ipfix_message_header_t *h;
     237             :   ipfix_set_header_t *s;
     238             :   ipfix_template_header_t *t;
     239             :   ipfix_field_specifier_t *f;
     240             :   ipfix_field_specifier_t *first_field;
     241          59 :   u8 *rewrite = 0;
     242             :   ip4_ipfix_template_packet_t *tp;
     243          59 :   u32 field_count = 0;
     244             :   flow_report_stream_t *stream;
     245          59 :   flowprobe_main_t *fm = &flowprobe_main;
     246          59 :   flowprobe_record_t flags = fr->opaque.as_uword;
     247          59 :   bool collect_ip4 = false, collect_ip6 = false;
     248             : 
     249          59 :   stream = &exp->streams[fr->stream_index];
     250             : 
     251          59 :   if (flags & FLOW_RECORD_L3)
     252             :     {
     253          43 :       collect_ip4 = which == FLOW_VARIANT_L2_IP4 || which == FLOW_VARIANT_IP4;
     254          43 :       collect_ip6 = which == FLOW_VARIANT_L2_IP6 || which == FLOW_VARIANT_IP6;
     255          43 :       if (which == FLOW_VARIANT_L2_IP4)
     256          12 :         flags |= FLOW_RECORD_L2_IP4;
     257          43 :       if (which == FLOW_VARIANT_L2_IP6)
     258          12 :         flags |= FLOW_RECORD_L2_IP6;
     259             :     }
     260             : 
     261          59 :   field_count += flowprobe_template_common_field_count ();
     262          59 :   if (flags & FLOW_RECORD_L2)
     263          43 :     field_count += flowprobe_template_l2_field_count ();
     264          59 :   if (collect_ip4)
     265          17 :     field_count += flowprobe_template_ip4_field_count ();
     266          59 :   if (collect_ip6)
     267          16 :     field_count += flowprobe_template_ip6_field_count ();
     268          59 :   if (flags & FLOW_RECORD_L4)
     269          43 :     field_count += flowprobe_template_l4_field_count ();
     270             : 
     271             :   /* allocate rewrite space */
     272          59 :   vec_validate_aligned
     273             :     (rewrite, sizeof (ip4_ipfix_template_packet_t)
     274             :      + field_count * sizeof (ipfix_field_specifier_t) - 1,
     275             :      CLIB_CACHE_LINE_BYTES);
     276             : 
     277          59 :   tp = (ip4_ipfix_template_packet_t *) rewrite;
     278          59 :   ip = (ip4_header_t *) & tp->ip4;
     279          59 :   udp = (udp_header_t *) (ip + 1);
     280          59 :   h = (ipfix_message_header_t *) (udp + 1);
     281          59 :   s = (ipfix_set_header_t *) (h + 1);
     282          59 :   t = (ipfix_template_header_t *) (s + 1);
     283          59 :   first_field = f = (ipfix_field_specifier_t *) (t + 1);
     284             : 
     285          59 :   ip->ip_version_and_header_length = 0x45;
     286          59 :   ip->ttl = 254;
     287          59 :   ip->protocol = IP_PROTOCOL_UDP;
     288          59 :   ip->src_address.as_u32 = exp->src_address.ip.ip4.as_u32;
     289          59 :   ip->dst_address.as_u32 = exp->ipfix_collector.ip.ip4.as_u32;
     290          59 :   udp->src_port = clib_host_to_net_u16 (stream->src_port);
     291          59 :   udp->dst_port = clib_host_to_net_u16 (collector_port);
     292          59 :   udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip));
     293             : 
     294             :   /* FIXUP: message header export_time */
     295             :   /* FIXUP: message header sequence_number */
     296          59 :   h->domain_id = clib_host_to_net_u32 (stream->domain_id);
     297             : 
     298             :   /* Add TLVs to the template */
     299          59 :   f = flowprobe_template_common_fields (f);
     300             : 
     301          59 :   if (flags & FLOW_RECORD_L2)
     302          43 :     f = flowprobe_template_l2_fields (f);
     303          59 :   if (collect_ip4)
     304          17 :     f = flowprobe_template_ip4_fields (f);
     305          59 :   if (collect_ip6)
     306          16 :     f = flowprobe_template_ip6_fields (f);
     307          59 :   if (flags & FLOW_RECORD_L4)
     308          43 :     f = flowprobe_template_l4_fields (f);
     309             : 
     310             :   /* Back to the template packet... */
     311          59 :   ip = (ip4_header_t *) & tp->ip4;
     312          59 :   udp = (udp_header_t *) (ip + 1);
     313             : 
     314          59 :   ASSERT (f - first_field);
     315             :   /* Field count in this template */
     316          59 :   t->id_count = ipfix_id_count (fr->template_id, f - first_field);
     317             : 
     318          59 :   fm->template_size[flags] = (u8 *) f - (u8 *) s;
     319             : 
     320             :   /* set length in octets */
     321          59 :   s->set_id_length =
     322          59 :     ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
     323             : 
     324             :   /* message length in octets */
     325          59 :   h->version_length = version_length ((u8 *) f - (u8 *) h);
     326             : 
     327          59 :   ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip);
     328          59 :   ip->checksum = ip4_header_checksum (ip);
     329             : 
     330          59 :   return rewrite;
     331             : }
     332             : 
     333             : static u8 *
     334           8 : flowprobe_template_rewrite_ip6 (ipfix_exporter_t *exp, flow_report_t *fr,
     335             :                                 u16 collector_port,
     336             :                                 ipfix_report_element_t *elts, u32 n_elts,
     337             :                                 u32 *stream_index)
     338             : {
     339           8 :   return flowprobe_template_rewrite_inline (exp, fr, collector_port,
     340             :                                             FLOW_VARIANT_IP6);
     341             : }
     342             : 
     343             : static u8 *
     344           9 : flowprobe_template_rewrite_ip4 (ipfix_exporter_t *exp, flow_report_t *fr,
     345             :                                 u16 collector_port,
     346             :                                 ipfix_report_element_t *elts, u32 n_elts,
     347             :                                 u32 *stream_index)
     348             : {
     349           9 :   return flowprobe_template_rewrite_inline (exp, fr, collector_port,
     350             :                                             FLOW_VARIANT_IP4);
     351             : }
     352             : 
     353             : static u8 *
     354          14 : flowprobe_template_rewrite_l2 (ipfix_exporter_t *exp, flow_report_t *fr,
     355             :                                u16 collector_port,
     356             :                                ipfix_report_element_t *elts, u32 n_elts,
     357             :                                u32 *stream_index)
     358             : {
     359          14 :   return flowprobe_template_rewrite_inline (exp, fr, collector_port,
     360             :                                             FLOW_VARIANT_L2);
     361             : }
     362             : 
     363             : static u8 *
     364          14 : flowprobe_template_rewrite_l2_ip4 (ipfix_exporter_t *exp, flow_report_t *fr,
     365             :                                    u16 collector_port,
     366             :                                    ipfix_report_element_t *elts, u32 n_elts,
     367             :                                    u32 *stream_index)
     368             : {
     369          14 :   return flowprobe_template_rewrite_inline (exp, fr, collector_port,
     370             :                                             FLOW_VARIANT_L2_IP4);
     371             : }
     372             : 
     373             : static u8 *
     374          14 : flowprobe_template_rewrite_l2_ip6 (ipfix_exporter_t *exp, flow_report_t *fr,
     375             :                                    u16 collector_port,
     376             :                                    ipfix_report_element_t *elts, u32 n_elts,
     377             :                                    u32 *stream_index)
     378             : {
     379          14 :   return flowprobe_template_rewrite_inline (exp, fr, collector_port,
     380             :                                             FLOW_VARIANT_L2_IP6);
     381             : }
     382             : 
     383             : /**
     384             :  * @brief Flush accumulated data
     385             :  * @param frm flow_report_main_t *
     386             :  * @param fr flow_report_t *
     387             :  * @param f vlib_frame_t *
     388             :  *
     389             :  * <em>Notes:</em>
     390             :  * This function must simply return the incoming frame, or no template packets
     391             :  * will be sent.
     392             :  */
     393             : vlib_frame_t *
     394          18 : flowprobe_data_callback_ip4 (flow_report_main_t *frm, ipfix_exporter_t *exp,
     395             :                              flow_report_t *fr, vlib_frame_t *f, u32 *to_next,
     396             :                              u32 node_index)
     397             : {
     398          18 :   flowprobe_flush_callback_ip4 ();
     399          18 :   return f;
     400             : }
     401             : 
     402             : vlib_frame_t *
     403          14 : flowprobe_data_callback_ip6 (flow_report_main_t *frm, ipfix_exporter_t *exp,
     404             :                              flow_report_t *fr, vlib_frame_t *f, u32 *to_next,
     405             :                              u32 node_index)
     406             : {
     407          14 :   flowprobe_flush_callback_ip6 ();
     408          14 :   return f;
     409             : }
     410             : 
     411             : vlib_frame_t *
     412         108 : flowprobe_data_callback_l2 (flow_report_main_t *frm, ipfix_exporter_t *exp,
     413             :                             flow_report_t *fr, vlib_frame_t *f, u32 *to_next,
     414             :                             u32 node_index)
     415             : {
     416         108 :   flowprobe_flush_callback_l2 ();
     417         108 :   return f;
     418             : }
     419             : 
     420             : static int
     421         119 : flowprobe_template_add_del (u32 domain_id, u16 src_port,
     422             :                             flowprobe_record_t flags,
     423             :                             vnet_flow_data_callback_t * flow_data_callback,
     424             :                             vnet_flow_rewrite_callback_t * rewrite_callback,
     425             :                             bool is_add, u16 * template_id)
     426             : {
     427         119 :   ipfix_exporter_t *exp = &flow_report_main.exporters[0];
     428         119 :   vnet_flow_report_add_del_args_t a = {
     429             :     .rewrite_callback = rewrite_callback,
     430             :     .flow_data_callback = flow_data_callback,
     431             :     .is_add = is_add,
     432             :     .domain_id = domain_id,
     433             :     .src_port = src_port,
     434             :     .opaque.as_uword = flags,
     435             :   };
     436         119 :   return vnet_flow_report_add_del (exp, &a, template_id);
     437             : }
     438             : 
     439             : static void
     440           5 : flowprobe_expired_timer_callback (u32 * expired_timers)
     441             : {
     442           5 :   vlib_main_t *vm = vlib_get_main ();
     443           5 :   flowprobe_main_t *fm = &flowprobe_main;
     444           5 :   u32 my_cpu_number = vm->thread_index;
     445             :   int i;
     446             :   u32 poolindex;
     447             : 
     448          10 :   for (i = 0; i < vec_len (expired_timers); i++)
     449             :     {
     450           5 :       poolindex = expired_timers[i] & 0x7FFFFFFF;
     451           5 :       vec_add1 (fm->expired_passive_per_worker[my_cpu_number], poolindex);
     452             :     }
     453           5 : }
     454             : 
     455             : static clib_error_t *
     456           4 : flowprobe_create_state_tables (u32 active_timer)
     457             : {
     458           4 :   flowprobe_main_t *fm = &flowprobe_main;
     459           4 :   vlib_thread_main_t *tm = &vlib_thread_main;
     460           4 :   vlib_main_t *vm = vlib_get_main ();
     461           4 :   clib_error_t *error = 0;
     462             :   u32 num_threads;
     463             :   int i;
     464             : 
     465             :   /* Decide how many worker threads we have */
     466           4 :   num_threads = 1 /* main thread */  + tm->n_threads;
     467             : 
     468             :   /* Hash table per worker */
     469           4 :   fm->ht_log2len = FLOWPROBE_LOG2_HASHSIZE;
     470             : 
     471             :   /* Init per worker flow state and timer wheels */
     472           4 :   if (active_timer)
     473             :     {
     474           2 :       vec_validate (fm->timers_per_worker, num_threads - 1);
     475           2 :       vec_validate (fm->expired_passive_per_worker, num_threads - 1);
     476           2 :       vec_validate (fm->hash_per_worker, num_threads - 1);
     477           2 :       vec_validate (fm->pool_per_worker, num_threads - 1);
     478             : 
     479           4 :       for (i = 0; i < num_threads; i++)
     480             :         {
     481             :           int j;
     482           2 :           pool_alloc (fm->pool_per_worker[i], 1 << fm->ht_log2len);
     483           2 :           vec_resize (fm->hash_per_worker[i], 1 << fm->ht_log2len);
     484      524290 :           for (j = 0; j < (1 << fm->ht_log2len); j++)
     485      524288 :             fm->hash_per_worker[i][j] = ~0;
     486           4 :           fm->timers_per_worker[i] =
     487           2 :             clib_mem_alloc (sizeof (TWT (tw_timer_wheel)));
     488           2 :           tw_timer_wheel_init_2t_1w_2048sl (fm->timers_per_worker[i],
     489             :                                             flowprobe_expired_timer_callback,
     490             :                                             1.0, 1024);
     491             :         }
     492           2 :       fm->disabled = true;
     493             :     }
     494             :   else
     495             :     {
     496           2 :       f64 now = vlib_time_now (vm);
     497           2 :       vec_validate (fm->stateless_entry, num_threads - 1);
     498           4 :       for (i = 0; i < num_threads; i++)
     499           2 :         fm->stateless_entry[i].last_exported = now;
     500           2 :       fm->disabled = false;
     501             :     }
     502           4 :   fm->initialized = true;
     503           4 :   return error;
     504             : }
     505             : 
     506             : static int
     507          73 : validate_feature_on_interface (flowprobe_main_t * fm, u32 sw_if_index,
     508             :                                u8 which)
     509             : {
     510         102 :   vec_validate_init_empty (fm->flow_per_interface, sw_if_index, ~0);
     511         102 :   vec_validate_init_empty (fm->direction_per_interface, sw_if_index, ~0);
     512             : 
     513          73 :   if (fm->flow_per_interface[sw_if_index] == (u8) ~ 0)
     514          37 :     return -1;
     515          36 :   else if (fm->flow_per_interface[sw_if_index] != which)
     516           0 :     return 0;
     517             :   else
     518          36 :     return 1;
     519             : }
     520             : 
     521             : /**
     522             :  * @brief configure / deconfigure the IPFIX flow-per-packet
     523             :  * @param fm flowprobe_main_t * fm
     524             :  * @param sw_if_index u32 the desired interface
     525             :  * @param which u8 the desired datapath
     526             :  * @param direction u8 the desired direction
     527             :  * @param is_add int 1 to enable the feature, 0 to disable it
     528             :  * @returns 0 if successful, non-zero otherwise
     529             :  */
     530             : 
     531             : static int
     532          73 : flowprobe_interface_add_del_feature (flowprobe_main_t *fm, u32 sw_if_index,
     533             :                                      u8 which, u8 direction, int is_add)
     534             : {
     535          73 :   vlib_main_t *vm = vlib_get_main ();
     536          73 :   int rv = 0;
     537          73 :   u16 template_id = 0;
     538          73 :   flowprobe_record_t flags = fm->record;
     539             : 
     540          73 :   fm->flow_per_interface[sw_if_index] = (is_add) ? which : (u8) ~ 0;
     541          73 :   fm->direction_per_interface[sw_if_index] = (is_add) ? direction : (u8) ~0;
     542          73 :   fm->template_per_flow[which] += (is_add) ? 1 : -1;
     543          73 :   if (is_add && fm->template_per_flow[which] > 1)
     544           0 :     template_id = fm->template_reports[flags];
     545             : 
     546          73 :   if ((is_add && fm->template_per_flow[which] == 1) ||
     547          36 :       (!is_add && fm->template_per_flow[which] == 0))
     548             :     {
     549          73 :       if (which == FLOW_VARIANT_L2)
     550             :         {
     551          35 :           if (fm->record & FLOW_RECORD_L2)
     552             :             {
     553          27 :               rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags,
     554             :                                                flowprobe_data_callback_l2,
     555             :                                                flowprobe_template_rewrite_l2,
     556             :                                                is_add, &template_id);
     557             :             }
     558          35 :           if (fm->record & FLOW_RECORD_L3 || fm->record & FLOW_RECORD_L4)
     559             :             {
     560          27 :               rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags,
     561             :                                                flowprobe_data_callback_l2,
     562             :                                                flowprobe_template_rewrite_l2_ip4,
     563             :                                                is_add, &template_id);
     564          27 :               fm->template_reports[flags | FLOW_RECORD_L2_IP4] =
     565             :                 (is_add) ? template_id : 0;
     566             :               rv =
     567          27 :                 flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags,
     568             :                                             flowprobe_data_callback_l2,
     569             :                                             flowprobe_template_rewrite_l2_ip6,
     570             :                                             is_add, &template_id);
     571          27 :               fm->template_reports[flags | FLOW_RECORD_L2_IP6] =
     572             :                 (is_add) ? template_id : 0;
     573             : 
     574             :               /* Special case L2 */
     575          27 :               fm->context[FLOW_VARIANT_L2_IP4].flags =
     576          27 :                 flags | FLOW_RECORD_L2_IP4;
     577          27 :               fm->context[FLOW_VARIANT_L2_IP6].flags =
     578          27 :                 flags | FLOW_RECORD_L2_IP6;
     579             : 
     580          27 :               fm->template_reports[flags] = template_id;
     581             :             }
     582             :         }
     583          38 :       else if (which == FLOW_VARIANT_IP4)
     584          20 :         rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags,
     585             :                                          flowprobe_data_callback_ip4,
     586             :                                          flowprobe_template_rewrite_ip4,
     587             :                                          is_add, &template_id);
     588          18 :       else if (which == FLOW_VARIANT_IP6)
     589          18 :         rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags,
     590             :                                          flowprobe_data_callback_ip6,
     591             :                                          flowprobe_template_rewrite_ip6,
     592             :                                          is_add, &template_id);
     593             :     }
     594          73 :   if (rv && rv != VNET_API_ERROR_VALUE_EXIST)
     595             :     {
     596           0 :       clib_warning ("vnet_flow_report_add_del returned %d", rv);
     597           0 :       return -1;
     598             :     }
     599             : 
     600          73 :   if (which != (u8) ~ 0)
     601             :     {
     602          73 :       fm->context[which].flags = fm->record;
     603          73 :       fm->template_reports[flags] = (is_add) ? template_id : 0;
     604             :     }
     605             : 
     606          73 :   if (direction == FLOW_DIRECTION_RX || direction == FLOW_DIRECTION_BOTH)
     607             :     {
     608          32 :       if (which == FLOW_VARIANT_IP4)
     609             :         {
     610           8 :           vnet_feature_enable_disable ("ip4-unicast", "flowprobe-input-ip4",
     611             :                                        sw_if_index, is_add, 0, 0);
     612           8 :           vnet_feature_enable_disable ("ip4-multicast", "flowprobe-input-ip4",
     613             :                                        sw_if_index, is_add, 0, 0);
     614             :         }
     615          24 :       else if (which == FLOW_VARIANT_IP6)
     616             :         {
     617          10 :           vnet_feature_enable_disable ("ip6-unicast", "flowprobe-input-ip6",
     618             :                                        sw_if_index, is_add, 0, 0);
     619          10 :           vnet_feature_enable_disable ("ip6-multicast", "flowprobe-input-ip6",
     620             :                                        sw_if_index, is_add, 0, 0);
     621             :         }
     622          14 :       else if (which == FLOW_VARIANT_L2)
     623          14 :         vnet_feature_enable_disable ("device-input", "flowprobe-input-l2",
     624             :                                      sw_if_index, is_add, 0, 0);
     625             :     }
     626             : 
     627          73 :   if (direction == FLOW_DIRECTION_TX || direction == FLOW_DIRECTION_BOTH)
     628             :     {
     629          43 :       if (which == FLOW_VARIANT_IP4)
     630          12 :         vnet_feature_enable_disable ("ip4-output", "flowprobe-output-ip4",
     631             :                                      sw_if_index, is_add, 0, 0);
     632          31 :       else if (which == FLOW_VARIANT_IP6)
     633          10 :         vnet_feature_enable_disable ("ip6-output", "flowprobe-output-ip6",
     634             :                                      sw_if_index, is_add, 0, 0);
     635          21 :       else if (which == FLOW_VARIANT_L2)
     636          21 :         vnet_feature_enable_disable ("interface-output", "flowprobe-output-l2",
     637             :                                      sw_if_index, is_add, 0, 0);
     638             :     }
     639             : 
     640             :   /* Stateful flow collection */
     641          73 :   if (is_add && !fm->initialized)
     642             :     {
     643           4 :       flowprobe_create_state_tables (fm->active_timer);
     644           4 :       if (fm->active_timer)
     645           2 :         vlib_process_signal_event (vm, flowprobe_timer_node.index, 1, 0);
     646             :     }
     647             : 
     648          73 :   return 0;
     649             : }
     650             : 
     651             : /**
     652             :  * @brief API message handler
     653             :  * @param mp vl_api_flowprobe_tx_interface_add_del_t * mp the api message
     654             :  */
     655           0 : void vl_api_flowprobe_tx_interface_add_del_t_handler
     656             :   (vl_api_flowprobe_tx_interface_add_del_t * mp)
     657             : {
     658           0 :   flowprobe_main_t *fm = &flowprobe_main;
     659             :   vl_api_flowprobe_tx_interface_add_del_reply_t *rmp;
     660           0 :   u32 sw_if_index = ntohl (mp->sw_if_index);
     661           0 :   int rv = 0;
     662             : 
     663           0 :   VALIDATE_SW_IF_INDEX (mp);
     664             : 
     665           0 :   if (fm->record == 0)
     666             :     {
     667           0 :       clib_warning ("Please specify flowprobe params record first...");
     668           0 :       rv = VNET_API_ERROR_CANNOT_ENABLE_DISABLE_FEATURE;
     669           0 :       goto out;
     670             :     }
     671             : 
     672           0 :   rv = validate_feature_on_interface (fm, sw_if_index, mp->which);
     673           0 :   if ((rv == 1 && mp->is_add == 1) || rv == 0)
     674             :     {
     675           0 :       rv = VNET_API_ERROR_CANNOT_ENABLE_DISABLE_FEATURE;
     676           0 :       goto out;
     677             :     }
     678             : 
     679           0 :   rv = flowprobe_interface_add_del_feature (fm, sw_if_index, mp->which,
     680           0 :                                             FLOW_DIRECTION_TX, mp->is_add);
     681             : 
     682           0 : out:
     683           0 :   BAD_SW_IF_INDEX_LABEL;
     684             : 
     685           0 :   REPLY_MACRO (VL_API_FLOWPROBE_TX_INTERFACE_ADD_DEL_REPLY);
     686             : }
     687             : 
     688             : void
     689          73 : vl_api_flowprobe_interface_add_del_t_handler (
     690             :   vl_api_flowprobe_interface_add_del_t *mp)
     691             : {
     692          73 :   flowprobe_main_t *fm = &flowprobe_main;
     693             :   vl_api_flowprobe_interface_add_del_reply_t *rmp;
     694             :   u32 sw_if_index;
     695             :   u8 which;
     696             :   u8 direction;
     697             :   bool is_add;
     698          73 :   int rv = 0;
     699             : 
     700          73 :   VALIDATE_SW_IF_INDEX (mp);
     701             : 
     702          73 :   sw_if_index = ntohl (mp->sw_if_index);
     703          73 :   is_add = mp->is_add;
     704             : 
     705          73 :   if (mp->which == FLOWPROBE_WHICH_IP4)
     706          20 :     which = FLOW_VARIANT_IP4;
     707          53 :   else if (mp->which == FLOWPROBE_WHICH_IP6)
     708          18 :     which = FLOW_VARIANT_IP6;
     709          35 :   else if (mp->which == FLOWPROBE_WHICH_L2)
     710          35 :     which = FLOW_VARIANT_L2;
     711             :   else
     712             :     {
     713           0 :       clib_warning ("Invalid value of which");
     714           0 :       rv = VNET_API_ERROR_INVALID_VALUE;
     715           0 :       goto out;
     716             :     }
     717             : 
     718          73 :   if (mp->direction == FLOWPROBE_DIRECTION_RX)
     719          30 :     direction = FLOW_DIRECTION_RX;
     720          43 :   else if (mp->direction == FLOWPROBE_DIRECTION_TX)
     721          41 :     direction = FLOW_DIRECTION_TX;
     722           2 :   else if (mp->direction == FLOWPROBE_DIRECTION_BOTH)
     723           2 :     direction = FLOW_DIRECTION_BOTH;
     724             :   else
     725             :     {
     726           0 :       clib_warning ("Invalid value of direction");
     727           0 :       rv = VNET_API_ERROR_INVALID_VALUE;
     728           0 :       goto out;
     729             :     }
     730             : 
     731          73 :   if (fm->record == 0)
     732             :     {
     733           0 :       clib_warning ("Please specify flowprobe params record first");
     734           0 :       rv = VNET_API_ERROR_CANNOT_ENABLE_DISABLE_FEATURE;
     735           0 :       goto out;
     736             :     }
     737             : 
     738          73 :   rv = validate_feature_on_interface (fm, sw_if_index, which);
     739          73 :   if (rv == 1)
     740             :     {
     741          36 :       if (is_add)
     742             :         {
     743           0 :           clib_warning ("Variant is already enabled for given interface");
     744           0 :           rv = VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
     745           0 :           goto out;
     746             :         }
     747             :     }
     748          37 :   else if (rv == 0)
     749             :     {
     750           0 :       clib_warning ("Interface has different variant enabled");
     751           0 :       rv = VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
     752           0 :       goto out;
     753             :     }
     754          37 :   else if (rv == -1)
     755             :     {
     756          37 :       if (!is_add)
     757             :         {
     758           0 :           clib_warning ("Interface has no variant enabled");
     759           0 :           rv = VNET_API_ERROR_NO_SUCH_ENTRY;
     760           0 :           goto out;
     761             :         }
     762             :     }
     763             : 
     764          73 :   rv = flowprobe_interface_add_del_feature (fm, sw_if_index, which, direction,
     765             :                                             is_add);
     766             : 
     767          73 : out:
     768          73 :   BAD_SW_IF_INDEX_LABEL;
     769             : 
     770          73 :   REPLY_MACRO (VL_API_FLOWPROBE_INTERFACE_ADD_DEL_REPLY);
     771             : }
     772             : 
     773             : static void
     774           4 : send_flowprobe_interface_details (u32 sw_if_index, u8 which, u8 direction,
     775             :                                   vl_api_registration_t *reg, u32 context)
     776             : {
     777           4 :   flowprobe_main_t *fm = &flowprobe_main;
     778           4 :   vl_api_flowprobe_interface_details_t *rmp = 0;
     779             : 
     780           4 :   rmp = vl_msg_api_alloc (sizeof (*rmp));
     781           4 :   if (!rmp)
     782           0 :     return;
     783           4 :   clib_memset (rmp, 0, sizeof (*rmp));
     784           4 :   rmp->_vl_msg_id =
     785           4 :     ntohs (VL_API_FLOWPROBE_INTERFACE_DETAILS + REPLY_MSG_ID_BASE);
     786           4 :   rmp->context = context;
     787             : 
     788           4 :   rmp->sw_if_index = htonl (sw_if_index);
     789             : 
     790           4 :   if (which == FLOW_VARIANT_IP4)
     791           2 :     rmp->which = FLOWPROBE_WHICH_IP4;
     792           2 :   else if (which == FLOW_VARIANT_IP6)
     793           1 :     rmp->which = FLOWPROBE_WHICH_IP6;
     794           1 :   else if (which == FLOW_VARIANT_L2)
     795           1 :     rmp->which = FLOWPROBE_WHICH_L2;
     796             :   else
     797           0 :     ASSERT (0);
     798             : 
     799           4 :   if (direction == FLOW_DIRECTION_RX)
     800           1 :     rmp->direction = FLOWPROBE_DIRECTION_RX;
     801           3 :   else if (direction == FLOW_DIRECTION_TX)
     802           2 :     rmp->direction = FLOWPROBE_DIRECTION_TX;
     803           1 :   else if (direction == FLOW_DIRECTION_BOTH)
     804           1 :     rmp->direction = FLOWPROBE_DIRECTION_BOTH;
     805             :   else
     806           0 :     ASSERT (0);
     807             : 
     808           4 :   vl_api_send_msg (reg, (u8 *) rmp);
     809             : }
     810             : 
     811             : static void
     812           3 : vl_api_flowprobe_interface_dump_t_handler (
     813             :   vl_api_flowprobe_interface_dump_t *mp)
     814             : {
     815           3 :   flowprobe_main_t *fm = &flowprobe_main;
     816             :   vl_api_registration_t *reg;
     817             :   u32 sw_if_index;
     818             : 
     819           3 :   reg = vl_api_client_index_to_registration (mp->client_index);
     820           3 :   if (!reg)
     821           0 :     return;
     822             : 
     823           3 :   sw_if_index = ntohl (mp->sw_if_index);
     824             : 
     825           3 :   if (sw_if_index == ~0)
     826             :     {
     827             :       u8 *which;
     828             : 
     829          11 :       vec_foreach (which, fm->flow_per_interface)
     830             :         {
     831          10 :           if (*which == (u8) ~0)
     832           7 :             continue;
     833             : 
     834           3 :           sw_if_index = which - fm->flow_per_interface;
     835           3 :           send_flowprobe_interface_details (
     836           3 :             sw_if_index, *which, fm->direction_per_interface[sw_if_index], reg,
     837             :             mp->context);
     838             :         }
     839             :     }
     840           2 :   else if (vec_len (fm->flow_per_interface) > sw_if_index &&
     841           1 :            fm->flow_per_interface[sw_if_index] != (u8) ~0)
     842             :     {
     843           1 :       send_flowprobe_interface_details (
     844           1 :         sw_if_index, fm->flow_per_interface[sw_if_index],
     845           1 :         fm->direction_per_interface[sw_if_index], reg, mp->context);
     846             :     }
     847             : }
     848             : 
     849             : #define vec_neg_search(v,E)         \
     850             : ({              \
     851             :   word _v(i) = 0;         \
     852             :   while (_v(i) < vec_len(v) && v[_v(i)] == E)        \
     853             :   {             \
     854             :     _v(i)++;            \
     855             :   }             \
     856             :   if (_v(i) == vec_len(v))        \
     857             :     _v(i) = ~0;                 \
     858             :   _v(i);            \
     859             : })
     860             : 
     861             : static int
     862          38 : flowprobe_params (flowprobe_main_t * fm, u8 record_l2,
     863             :                   u8 record_l3, u8 record_l4,
     864             :                   u32 active_timer, u32 passive_timer)
     865             : {
     866          38 :   flowprobe_record_t flags = 0;
     867             : 
     868         254 :   if (vec_neg_search (fm->flow_per_interface, (u8) ~ 0) != ~0)
     869           3 :     return VNET_API_ERROR_UNSUPPORTED;
     870             : 
     871          35 :   if (record_l2)
     872          23 :     flags |= FLOW_RECORD_L2;
     873          35 :   if (record_l3)
     874          21 :     flags |= FLOW_RECORD_L3;
     875          35 :   if (record_l4)
     876          21 :     flags |= FLOW_RECORD_L4;
     877             : 
     878          35 :   fm->record = flags;
     879             : 
     880             :   /*
     881             :    * Timers: ~0 is default, 0 is off
     882             :    */
     883          35 :   fm->active_timer =
     884          35 :     (active_timer == (u32) ~ 0 ? FLOWPROBE_TIMER_ACTIVE : active_timer);
     885          35 :   fm->passive_timer =
     886          35 :     (passive_timer == (u32) ~ 0 ? FLOWPROBE_TIMER_PASSIVE : passive_timer);
     887             : 
     888          35 :   return 0;
     889             : }
     890             : 
     891             : void
     892           0 : vl_api_flowprobe_params_t_handler (vl_api_flowprobe_params_t * mp)
     893             : {
     894           0 :   flowprobe_main_t *fm = &flowprobe_main;
     895             :   vl_api_flowprobe_params_reply_t *rmp;
     896           0 :   int rv = 0;
     897             : 
     898           0 :   rv = flowprobe_params
     899             :     (fm,
     900           0 :      mp->record_flags & FLOWPROBE_RECORD_FLAG_L2,
     901           0 :      mp->record_flags & FLOWPROBE_RECORD_FLAG_L3,
     902           0 :      mp->record_flags & FLOWPROBE_RECORD_FLAG_L4,
     903             :      clib_net_to_host_u32 (mp->active_timer),
     904             :      clib_net_to_host_u32 (mp->passive_timer));
     905             : 
     906           0 :   REPLY_MACRO (VL_API_FLOWPROBE_PARAMS_REPLY);
     907             : }
     908             : 
     909             : void
     910          38 : vl_api_flowprobe_set_params_t_handler (vl_api_flowprobe_set_params_t *mp)
     911             : {
     912          38 :   flowprobe_main_t *fm = &flowprobe_main;
     913             :   vl_api_flowprobe_set_params_reply_t *rmp;
     914             :   bool record_l2, record_l3, record_l4;
     915             :   u32 active_timer;
     916             :   u32 passive_timer;
     917          38 :   int rv = 0;
     918             : 
     919          38 :   record_l2 = (mp->record_flags & FLOWPROBE_RECORD_FLAG_L2);
     920          38 :   record_l3 = (mp->record_flags & FLOWPROBE_RECORD_FLAG_L3);
     921          38 :   record_l4 = (mp->record_flags & FLOWPROBE_RECORD_FLAG_L4);
     922             : 
     923          38 :   active_timer = clib_net_to_host_u32 (mp->active_timer);
     924          38 :   passive_timer = clib_net_to_host_u32 (mp->passive_timer);
     925             : 
     926          38 :   if (passive_timer > 0 && active_timer > passive_timer)
     927             :     {
     928           0 :       clib_warning ("Passive timer must be greater than active timer");
     929           0 :       rv = VNET_API_ERROR_INVALID_VALUE;
     930           0 :       goto out;
     931             :     }
     932             : 
     933          38 :   rv = flowprobe_params (fm, record_l2, record_l3, record_l4, active_timer,
     934             :                          passive_timer);
     935          38 :   if (rv == VNET_API_ERROR_UNSUPPORTED)
     936           3 :     clib_warning (
     937             :       "Cannot change params when feature is enabled on some interfaces");
     938             : 
     939          35 : out:
     940          38 :   REPLY_MACRO (VL_API_FLOWPROBE_SET_PARAMS_REPLY);
     941             : }
     942             : 
     943             : void
     944           1 : vl_api_flowprobe_get_params_t_handler (vl_api_flowprobe_get_params_t *mp)
     945             : {
     946           1 :   flowprobe_main_t *fm = &flowprobe_main;
     947             :   vl_api_flowprobe_get_params_reply_t *rmp;
     948           1 :   u8 record_flags = 0;
     949           1 :   int rv = 0;
     950             : 
     951           1 :   if (fm->record & FLOW_RECORD_L2)
     952           1 :     record_flags |= FLOWPROBE_RECORD_FLAG_L2;
     953           1 :   if (fm->record & FLOW_RECORD_L3)
     954           1 :     record_flags |= FLOWPROBE_RECORD_FLAG_L3;
     955           1 :   if (fm->record & FLOW_RECORD_L4)
     956           1 :     record_flags |= FLOWPROBE_RECORD_FLAG_L4;
     957             : 
     958             :   // clang-format off
     959           1 :   REPLY_MACRO2 (VL_API_FLOWPROBE_GET_PARAMS_REPLY,
     960             :   ({
     961             :     rmp->record_flags = record_flags;
     962             :     rmp->active_timer = htonl (fm->active_timer);
     963             :     rmp->passive_timer = htonl (fm->passive_timer);
     964             :   }));
     965             :   // clang-format on
     966             : }
     967             : 
     968             : /* *INDENT-OFF* */
     969             : VLIB_PLUGIN_REGISTER () = {
     970             :     .version = VPP_BUILD_VER,
     971             :     .description = "Flow per Packet",
     972             : };
     973             : /* *INDENT-ON* */
     974             : 
     975             : u8 *
     976           0 : format_flowprobe_direction (u8 *s, va_list *args)
     977             : {
     978           0 :   u8 *direction = va_arg (*args, u8 *);
     979           0 :   if (*direction == FLOW_DIRECTION_RX)
     980           0 :     s = format (s, "rx");
     981           0 :   else if (*direction == FLOW_DIRECTION_TX)
     982           0 :     s = format (s, "tx");
     983           0 :   else if (*direction == FLOW_DIRECTION_BOTH)
     984           0 :     s = format (s, "rx tx");
     985             : 
     986           0 :   return s;
     987             : }
     988             : 
     989             : u8 *
     990           0 : format_flowprobe_entry (u8 * s, va_list * args)
     991             : {
     992           0 :   flowprobe_entry_t *e = va_arg (*args, flowprobe_entry_t *);
     993           0 :   s = format (s, " %U", format_flowprobe_direction, &e->key.direction);
     994           0 :   s = format (s, " %d/%d", e->key.rx_sw_if_index, e->key.tx_sw_if_index);
     995             : 
     996           0 :   s = format (s, " %U %U", format_ethernet_address, &e->key.src_mac,
     997             :               format_ethernet_address, &e->key.dst_mac);
     998           0 :   s = format (s, " %U -> %U",
     999             :               format_ip46_address, &e->key.src_address, IP46_TYPE_ANY,
    1000             :               format_ip46_address, &e->key.dst_address, IP46_TYPE_ANY);
    1001           0 :   s = format (s, " %d", e->key.protocol);
    1002           0 :   s = format (s, " %d %d\n", clib_net_to_host_u16 (e->key.src_port),
    1003           0 :               clib_net_to_host_u16 (e->key.dst_port));
    1004             : 
    1005           0 :   return s;
    1006             : }
    1007             : 
    1008             : u8 *
    1009           0 : format_flowprobe_feature (u8 * s, va_list * args)
    1010             : {
    1011           0 :   u8 *which = va_arg (*args, u8 *);
    1012           0 :   if (*which == FLOW_VARIANT_IP4)
    1013           0 :     s = format (s, "ip4");
    1014           0 :   else if (*which == FLOW_VARIANT_IP6)
    1015           0 :     s = format (s, "ip6");
    1016           0 :   else if (*which == FLOW_VARIANT_L2)
    1017           0 :     s = format (s, "l2");
    1018             : 
    1019           0 :   return s;
    1020             : }
    1021             : 
    1022             : u8 *
    1023           0 : format_flowprobe_params (u8 * s, va_list * args)
    1024             : {
    1025           0 :   flowprobe_record_t flags = va_arg (*args, flowprobe_record_t);
    1026           0 :   u32 active_timer = va_arg (*args, u32);
    1027           0 :   u32 passive_timer = va_arg (*args, u32);
    1028             : 
    1029           0 :   if (flags & FLOW_RECORD_L2)
    1030           0 :     s = format (s, " l2");
    1031           0 :   if (flags & FLOW_RECORD_L3)
    1032           0 :     s = format (s, " l3");
    1033           0 :   if (flags & FLOW_RECORD_L4)
    1034           0 :     s = format (s, " l4");
    1035             : 
    1036           0 :   if (active_timer != (u32) ~ 0)
    1037           0 :     s = format (s, " active: %d", active_timer);
    1038             : 
    1039           0 :   if (passive_timer != (u32) ~ 0)
    1040           0 :     s = format (s, " passive: %d", passive_timer);
    1041             : 
    1042           0 :   return s;
    1043             : }
    1044             : 
    1045             : static clib_error_t *
    1046           0 : flowprobe_show_table_fn (vlib_main_t * vm,
    1047             :                          unformat_input_t * input, vlib_cli_command_t * cm)
    1048             : {
    1049           0 :   flowprobe_main_t *fm = &flowprobe_main;
    1050             :   int i;
    1051             :   flowprobe_entry_t *e;
    1052             : 
    1053           0 :   vlib_cli_output (vm, "Dumping IPFIX table");
    1054             : 
    1055           0 :   for (i = 0; i < vec_len (fm->pool_per_worker); i++)
    1056             :     {
    1057             :       /* *INDENT-OFF* */
    1058           0 :       pool_foreach (e, fm->pool_per_worker[i])
    1059             :         {
    1060           0 :           vlib_cli_output (vm, "%U",
    1061             :                            format_flowprobe_entry,
    1062             :                            e);
    1063             :         }
    1064             :       /* *INDENT-ON* */
    1065             : 
    1066             :     }
    1067           0 :   return 0;
    1068             : }
    1069             : 
    1070             : static clib_error_t *
    1071           0 : flowprobe_show_stats_fn (vlib_main_t * vm,
    1072             :                          unformat_input_t * input, vlib_cli_command_t * cm)
    1073             : {
    1074           0 :   flowprobe_main_t *fm = &flowprobe_main;
    1075             :   int i;
    1076             : 
    1077           0 :   vlib_cli_output (vm, "IPFIX table statistics");
    1078           0 :   vlib_cli_output (vm, "Flow entry size: %d\n", sizeof (flowprobe_entry_t));
    1079           0 :   vlib_cli_output (vm, "Flow pool size per thread: %d\n",
    1080             :                    0x1 << FLOWPROBE_LOG2_HASHSIZE);
    1081             : 
    1082           0 :   for (i = 0; i < vec_len (fm->pool_per_worker); i++)
    1083           0 :     vlib_cli_output (vm, "Pool utilisation thread %d is %d%%\n", i,
    1084           0 :                      (100 * pool_elts (fm->pool_per_worker[i])) /
    1085             :                      (0x1 << FLOWPROBE_LOG2_HASHSIZE));
    1086           0 :   return 0;
    1087             : }
    1088             : 
    1089             : static clib_error_t *
    1090           0 : flowprobe_interface_add_del_feature_command_fn (vlib_main_t *vm,
    1091             :                                                 unformat_input_t *input,
    1092             :                                                 vlib_cli_command_t *cmd)
    1093             : {
    1094           0 :   flowprobe_main_t *fm = &flowprobe_main;
    1095           0 :   u32 sw_if_index = ~0;
    1096           0 :   int is_add = 1;
    1097           0 :   u8 which = FLOW_VARIANT_IP4;
    1098           0 :   flowprobe_direction_t direction = FLOW_DIRECTION_TX;
    1099             :   int rv;
    1100             : 
    1101           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    1102             :     {
    1103           0 :       if (unformat (input, "disable"))
    1104           0 :         is_add = 0;
    1105           0 :       else if (unformat (input, "%U", unformat_vnet_sw_interface,
    1106             :                          fm->vnet_main, &sw_if_index));
    1107           0 :       else if (unformat (input, "ip4"))
    1108           0 :         which = FLOW_VARIANT_IP4;
    1109           0 :       else if (unformat (input, "ip6"))
    1110           0 :         which = FLOW_VARIANT_IP6;
    1111           0 :       else if (unformat (input, "l2"))
    1112           0 :         which = FLOW_VARIANT_L2;
    1113           0 :       else if (unformat (input, "rx"))
    1114           0 :         direction = FLOW_DIRECTION_RX;
    1115           0 :       else if (unformat (input, "tx"))
    1116           0 :         direction = FLOW_DIRECTION_TX;
    1117           0 :       else if (unformat (input, "both"))
    1118           0 :         direction = FLOW_DIRECTION_BOTH;
    1119             :       else
    1120           0 :         break;
    1121             :     }
    1122             : 
    1123           0 :   if (fm->record == 0)
    1124           0 :     return clib_error_return (0,
    1125             :                               "Please specify flowprobe params record first...");
    1126             : 
    1127           0 :   if (sw_if_index == ~0)
    1128           0 :     return clib_error_return (0, "Please specify an interface...");
    1129             : 
    1130           0 :   rv = validate_feature_on_interface (fm, sw_if_index, which);
    1131           0 :   if (rv == 1)
    1132             :     {
    1133           0 :       if (is_add)
    1134           0 :         return clib_error_return (0,
    1135             :                                   "Datapath is already enabled for given interface...");
    1136             :     }
    1137           0 :   else if (rv == 0)
    1138           0 :     return clib_error_return (0,
    1139             :                               "Interface has enable different datapath ...");
    1140           0 :   else if (rv == -1)
    1141             :     {
    1142           0 :       if (!is_add)
    1143             :         {
    1144           0 :           return clib_error_return (0, "Interface has no datapath enabled");
    1145             :         }
    1146             :     }
    1147             : 
    1148           0 :   rv = flowprobe_interface_add_del_feature (fm, sw_if_index, which, direction,
    1149             :                                             is_add);
    1150           0 :   switch (rv)
    1151             :     {
    1152           0 :     case 0:
    1153           0 :       break;
    1154             : 
    1155           0 :     case VNET_API_ERROR_INVALID_SW_IF_INDEX:
    1156           0 :       return clib_error_return
    1157             :         (0, "Invalid interface, only works on physical ports");
    1158             :       break;
    1159             : 
    1160           0 :     case VNET_API_ERROR_UNIMPLEMENTED:
    1161           0 :       return clib_error_return (0, "ip6 not supported");
    1162             :       break;
    1163             : 
    1164           0 :     default:
    1165           0 :       return clib_error_return (0, "flowprobe_enable_disable returned %d",
    1166             :                                 rv);
    1167             :     }
    1168           0 :   return 0;
    1169             : }
    1170             : 
    1171             : static clib_error_t *
    1172           0 : flowprobe_show_feature_command_fn (vlib_main_t * vm,
    1173             :                                    unformat_input_t * input,
    1174             :                                    vlib_cli_command_t * cmd)
    1175             : {
    1176           0 :   flowprobe_main_t *fm = &flowprobe_main;
    1177             :   u8 *which;
    1178             :   u32 sw_if_index;
    1179             : 
    1180           0 :   vec_foreach (which, fm->flow_per_interface)
    1181             :   {
    1182           0 :     if (*which == (u8) ~ 0)
    1183           0 :       continue;
    1184             : 
    1185           0 :     sw_if_index = which - fm->flow_per_interface;
    1186           0 :     vlib_cli_output (vm, " %U %U %U", format_vnet_sw_if_index_name,
    1187             :                      vnet_get_main (), sw_if_index, format_flowprobe_feature,
    1188             :                      which, format_flowprobe_direction,
    1189           0 :                      &fm->direction_per_interface[sw_if_index]);
    1190             :   }
    1191           0 :   return 0;
    1192             : }
    1193             : 
    1194             : static clib_error_t *
    1195           0 : flowprobe_params_command_fn (vlib_main_t * vm,
    1196             :                              unformat_input_t * input,
    1197             :                              vlib_cli_command_t * cmd)
    1198             : {
    1199           0 :   flowprobe_main_t *fm = &flowprobe_main;
    1200           0 :   bool record_l2 = false, record_l3 = false, record_l4 = false;
    1201           0 :   u32 active_timer = ~0;
    1202           0 :   u32 passive_timer = ~0;
    1203             : 
    1204           0 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    1205             :     {
    1206           0 :       if (unformat (input, "active %d", &active_timer))
    1207             :         ;
    1208           0 :       else if (unformat (input, "passive %d", &passive_timer))
    1209             :         ;
    1210           0 :       else if (unformat (input, "record"))
    1211           0 :         while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
    1212             :           {
    1213           0 :             if (unformat (input, "l2"))
    1214           0 :               record_l2 = true;
    1215           0 :             else if (unformat (input, "l3"))
    1216           0 :               record_l3 = true;
    1217           0 :             else if (unformat (input, "l4"))
    1218           0 :               record_l4 = true;
    1219             :             else
    1220           0 :               break;
    1221             :           }
    1222             :       else
    1223           0 :         break;
    1224             :     }
    1225             : 
    1226           0 :   if (passive_timer > 0 && active_timer > passive_timer)
    1227           0 :     return clib_error_return (0,
    1228             :                               "Passive timer has to be greater than active one...");
    1229             : 
    1230           0 :   if (flowprobe_params (fm, record_l2, record_l3, record_l4,
    1231             :                         active_timer, passive_timer))
    1232           0 :     return clib_error_return (0,
    1233             :                               "Couldn't change flowperpacket params when feature is enabled on some interface ...");
    1234           0 :   return 0;
    1235             : }
    1236             : 
    1237             : static clib_error_t *
    1238           0 : flowprobe_show_params_command_fn (vlib_main_t * vm,
    1239             :                                   unformat_input_t * input,
    1240             :                                   vlib_cli_command_t * cmd)
    1241             : {
    1242           0 :   flowprobe_main_t *fm = &flowprobe_main;
    1243           0 :   flowprobe_record_t flags = fm->record;
    1244           0 :   u32 active_timer = fm->active_timer;
    1245           0 :   u32 passive_timer = fm->passive_timer;
    1246             : 
    1247           0 :   vlib_cli_output (vm, "%U", format_flowprobe_params, flags, active_timer,
    1248             :                    passive_timer);
    1249           0 :   return 0;
    1250             : }
    1251             : 
    1252             : /*?
    1253             :  * '<em>flowprobe feature add-del</em>' commands to enable/disable
    1254             :  * per-packet IPFIX flow record generation on an interface
    1255             :  *
    1256             :  * @cliexpar
    1257             :  * @parblock
    1258             :  * To enable per-packet IPFIX flow-record generation on an interface:
    1259             :  * @cliexcmd{flowprobe feature add-del GigabitEthernet2/0/0}
    1260             :  *
    1261             :  * To disable per-packet IPFIX flow-record generation on an interface:
    1262             :  * @cliexcmd{flowprobe feature add-del GigabitEthernet2/0/0 disable}
    1263             :  * @cliexend
    1264             :  * @endparblock
    1265             : ?*/
    1266             : /* *INDENT-OFF* */
    1267      213527 : VLIB_CLI_COMMAND (flowprobe_enable_disable_command, static) = {
    1268             :   .path = "flowprobe feature add-del",
    1269             :   .short_help = "flowprobe feature add-del <interface-name> [(l2|ip4|ip6)] "
    1270             :                 "[(rx|tx|both)] [disable]",
    1271             :   .function = flowprobe_interface_add_del_feature_command_fn,
    1272             : };
    1273      213527 : VLIB_CLI_COMMAND (flowprobe_params_command, static) = {
    1274             :   .path = "flowprobe params",
    1275             :   .short_help = "flowprobe params record [l2] [l3] [l4] [active <timer>] "
    1276             :                 "[passive <timer>]",
    1277             :   .function = flowprobe_params_command_fn,
    1278             : };
    1279             : 
    1280      213527 : VLIB_CLI_COMMAND (flowprobe_show_feature_command, static) = {
    1281             :     .path = "show flowprobe feature",
    1282             :     .short_help =
    1283             :     "show flowprobe feature",
    1284             :     .function = flowprobe_show_feature_command_fn,
    1285             : };
    1286      213527 : VLIB_CLI_COMMAND (flowprobe_show_params_command, static) = {
    1287             :     .path = "show flowprobe params",
    1288             :     .short_help =
    1289             :     "show flowprobe params",
    1290             :     .function = flowprobe_show_params_command_fn,
    1291             : };
    1292      213527 : VLIB_CLI_COMMAND (flowprobe_show_table_command, static) = {
    1293             :     .path = "show flowprobe table",
    1294             :     .short_help = "show flowprobe table",
    1295             :     .function = flowprobe_show_table_fn,
    1296             : };
    1297      213527 : VLIB_CLI_COMMAND (flowprobe_show_stats_command, static) = {
    1298             :     .path = "show flowprobe statistics",
    1299             :     .short_help = "show flowprobe statistics",
    1300             :     .function = flowprobe_show_stats_fn,
    1301             : };
    1302             : /* *INDENT-ON* */
    1303             : 
    1304             : /*
    1305             :  * Main-core process, sending an interrupt to the per worker input
    1306             :  * process that spins the per worker timer wheel.
    1307             :  */
    1308             : static uword
    1309         559 : timer_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
    1310             : {
    1311         559 :   uword *event_data = 0;
    1312         559 :   vlib_main_t **worker_vms = 0, *worker_vm;
    1313         559 :   flowprobe_main_t *fm = &flowprobe_main;
    1314             : 
    1315             :   /* Wait for Godot... */
    1316         559 :   vlib_process_wait_for_event_or_clock (vm, 1e9);
    1317           2 :   uword event_type = vlib_process_get_events (vm, &event_data);
    1318           2 :   if (event_type != 1)
    1319           0 :     clib_warning ("bogus kickoff event received, %d", event_type);
    1320           2 :   vec_reset_length (event_data);
    1321             : 
    1322             :   int i;
    1323           2 :   if (vlib_get_n_threads () == 0)
    1324           0 :     vec_add1 (worker_vms, vm);
    1325             :   else
    1326             :     {
    1327           4 :       for (i = 0; i < vlib_get_n_threads (); i++)
    1328             :         {
    1329           2 :           worker_vm = vlib_get_main_by_index (i);
    1330           2 :           if (worker_vm)
    1331           2 :             vec_add1 (worker_vms, worker_vm);
    1332             :         }
    1333             :     }
    1334           2 :   f64 sleep_duration = 0.1;
    1335             : 
    1336             :   while (1)
    1337             :     {
    1338             :       /* Send an interrupt to each timer input node */
    1339      242708 :       sleep_duration = 0.1;
    1340      485416 :       for (i = 0; i < vec_len (worker_vms); i++)
    1341             :         {
    1342      242708 :           worker_vm = worker_vms[i];
    1343      242708 :           if (worker_vm)
    1344             :             {
    1345      242708 :               vlib_node_set_interrupt_pending (worker_vm,
    1346             :                                                flowprobe_walker_node.index);
    1347      242708 :               sleep_duration =
    1348      242708 :                 (fm->expired_passive_per_worker[i] > 0) ? 1e-4 : 0.1;
    1349             :             }
    1350             :         }
    1351      242708 :       vlib_process_suspend (vm, sleep_duration);
    1352             :     }
    1353             :   return 0;                     /* or not */
    1354             : }
    1355             : 
    1356             : /* *INDENT-OFF* */
    1357      139480 : VLIB_REGISTER_NODE (flowprobe_timer_node,static) = {
    1358             :   .function = timer_process,
    1359             :   .name = "flowprobe-timer-process",
    1360             :   .type = VLIB_NODE_TYPE_PROCESS,
    1361             : };
    1362             : /* *INDENT-ON* */
    1363             : 
    1364             : #include <flowprobe/flowprobe.api.c>
    1365             : 
    1366             : /**
    1367             :  * @brief Set up the API message handling tables
    1368             :  * @param vm vlib_main_t * vlib main data structure pointer
    1369             :  * @returns 0 to indicate all is well, or a clib_error_t
    1370             :  */
    1371             : static clib_error_t *
    1372         559 : flowprobe_init (vlib_main_t * vm)
    1373             : {
    1374         559 :   flowprobe_main_t *fm = &flowprobe_main;
    1375         559 :   vlib_thread_main_t *tm = &vlib_thread_main;
    1376         559 :   clib_error_t *error = 0;
    1377             :   u32 num_threads;
    1378             :   int i;
    1379             : 
    1380         559 :   fm->vnet_main = vnet_get_main ();
    1381             : 
    1382             :   /* Ask for a correctly-sized block of API message decode slots */
    1383         559 :   fm->msg_id_base = setup_message_id_table ();
    1384             : 
    1385             :   /* Set up time reference pair */
    1386         559 :   fm->vlib_time_0 = vlib_time_now (vm);
    1387         559 :   fm->nanosecond_time_0 = unix_time_now_nsec ();
    1388             : 
    1389         559 :   clib_memset (fm->template_reports, 0, sizeof (fm->template_reports));
    1390         559 :   clib_memset (fm->template_size, 0, sizeof (fm->template_size));
    1391         559 :   clib_memset (fm->template_per_flow, 0, sizeof (fm->template_per_flow));
    1392             : 
    1393             :   /* Decide how many worker threads we have */
    1394         559 :   num_threads = 1 /* main thread */  + tm->n_threads;
    1395             : 
    1396             :   /* Allocate per worker thread vectors per flavour */
    1397        3354 :   for (i = 0; i < FLOW_N_VARIANTS; i++)
    1398             :     {
    1399        2795 :       vec_validate (fm->context[i].buffers_per_worker, num_threads - 1);
    1400        2795 :       vec_validate (fm->context[i].frames_per_worker, num_threads - 1);
    1401        2795 :       vec_validate (fm->context[i].next_record_offset_per_worker,
    1402             :                     num_threads - 1);
    1403             :     }
    1404             : 
    1405         559 :   fm->active_timer = FLOWPROBE_TIMER_ACTIVE;
    1406         559 :   fm->passive_timer = FLOWPROBE_TIMER_PASSIVE;
    1407             : 
    1408         559 :   return error;
    1409             : }
    1410             : 
    1411        1119 : VLIB_INIT_FUNCTION (flowprobe_init);
    1412             : 
    1413             : /*
    1414             :  * fd.io coding-style-patch-verification: ON
    1415             :  *
    1416             :  * Local Variables:
    1417             :  * eval: (c-set-style "gnu")
    1418             :  * End:
    1419             :  */

Generated by: LCOV version 1.14