LCOV - code coverage report
Current view: top level - plugins/flowprobe - flowprobe.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 413 625 66.1 %
Date: 2023-10-26 01:39:38 Functions: 60 72 83.3 %

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

Generated by: LCOV version 1.14