LCOV - code coverage report
Current view: top level - vnet/ipfix-export - flow_api.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 128 238 53.8 %
Date: 2023-07-05 22:20:52 Functions: 12 17 70.6 %

          Line data    Source code
       1             : /*
       2             :  *------------------------------------------------------------------
       3             :  * flow_api.c - flow api
       4             :  *
       5             :  * Copyright (c) 2016 Cisco and/or its affiliates.
       6             :  * Licensed under the Apache License, Version 2.0 (the "License");
       7             :  * you may not use this file except in compliance with the License.
       8             :  * You may obtain a copy of the License at:
       9             :  *
      10             :  *     http://www.apache.org/licenses/LICENSE-2.0
      11             :  *
      12             :  * Unless required by applicable law or agreed to in writing, software
      13             :  * distributed under the License is distributed on an "AS IS" BASIS,
      14             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15             :  * See the License for the specific language governing permissions and
      16             :  * limitations under the License.
      17             :  *------------------------------------------------------------------
      18             :  */
      19             : 
      20             : #include <vnet/vnet.h>
      21             : #include <vlibmemory/api.h>
      22             : #include <vnet/ip/ip_types_api.h>
      23             : #include <vnet/udp/udp_local.h>
      24             : 
      25             : #include <vnet/interface.h>
      26             : #include <vnet/api_errno.h>
      27             : 
      28             : #include <vnet/fib/fib_table.h>
      29             : #include <vnet/ipfix-export/flow_report.h>
      30             : #include <vnet/ipfix-export/flow_report_classify.h>
      31             : 
      32             : #include <vnet/format_fns.h>
      33             : #include <vnet/ipfix-export/ipfix_export.api_enum.h>
      34             : #include <vnet/ipfix-export/ipfix_export.api_types.h>
      35             : 
      36             : #define REPLY_MSG_ID_BASE frm->msg_id_base
      37             : #include <vlibapi/api_helper_macros.h>
      38             : 
      39             : ipfix_exporter_t *
      40           5 : vnet_ipfix_exporter_lookup (const ip_address_t *ipfix_collector)
      41             : {
      42           5 :   flow_report_main_t *frm = &flow_report_main;
      43             :   ipfix_exporter_t *exp;
      44             : 
      45          11 :   pool_foreach (exp, frm->exporters)
      46             :     {
      47           9 :       if (ip_address_cmp (&exp->ipfix_collector, ipfix_collector) == 0)
      48           3 :         return exp;
      49             :     }
      50             : 
      51           2 :   return NULL;
      52             : }
      53             : 
      54             : /*
      55             :  * For backwards compatibility reasons index 0 in the set of exporters
      56             :  * is alwyas used for the exporter created via the set_ipfix_exporter
      57             :  * API.
      58             :  */
      59             : #define USE_INDEX_0   true
      60             : #define USE_ANY_INDEX false
      61             : 
      62             : static int
      63          48 : vl_api_set_ipfix_exporter_t_internal (
      64             :   u32 client_index, vl_api_address_t *mp_collector_address,
      65             :   u16 mp_collector_port, vl_api_address_t *mp_src_address, u32 mp_vrf_id,
      66             :   u32 mp_path_mtu, u32 mp_template_interval, bool mp_udp_checksum,
      67             :   bool use_index_0, bool is_create)
      68             : {
      69          48 :   vlib_main_t *vm = vlib_get_main ();
      70          48 :   flow_report_main_t *frm = &flow_report_main;
      71             :   ipfix_exporter_t *exp;
      72             :   vl_api_registration_t *reg;
      73             :   ip_address_t collector, src;
      74          48 :   u16 collector_port = UDP_DST_PORT_ipfix;
      75             :   u32 path_mtu;
      76             :   u32 template_interval;
      77             :   u8 udp_checksum;
      78             :   u32 fib_id;
      79          48 :   u32 fib_index = ~0;
      80             :   u32 ip_header_size;
      81             : 
      82          48 :   reg = vl_api_client_index_to_registration (client_index);
      83          48 :   if (!reg)
      84           0 :     return VNET_API_ERROR_UNIMPLEMENTED;
      85             : 
      86          48 :   if (use_index_0)
      87             :     {
      88             :       /*
      89             :        * In this case we update the existing exporter. There is no delete
      90             :        * for exp[0]
      91             :        */
      92          43 :       exp = &frm->exporters[0];
      93             : 
      94             :       /* Collector address must be IPv4 for exp[0] */
      95          43 :       collector.version = AF_IP4;
      96          43 :       ip4_address_decode (mp_collector_address->un.ip4, &collector.ip.ip4);
      97             :     }
      98             :   else
      99             :     {
     100           5 :       ip_address_decode2 (mp_collector_address, &collector);
     101           5 :       if (is_create)
     102             :         {
     103           3 :           exp = vnet_ipfix_exporter_lookup (&collector);
     104           3 :           if (!exp)
     105             :             {
     106             :               /* Create a new exporter instead of updating an existing one */
     107           2 :               if (pool_elts (frm->exporters) >= IPFIX_EXPORTERS_MAX)
     108           0 :                 return VNET_API_ERROR_INVALID_VALUE;
     109           2 :               pool_get (frm->exporters, exp);
     110             :             }
     111             :         }
     112             :       else
     113             :         {
     114             :           /* Delete the exporter */
     115           2 :           exp = vnet_ipfix_exporter_lookup (&collector);
     116           2 :           if (!exp)
     117           0 :             return VNET_API_ERROR_NO_SUCH_ENTRY;
     118             : 
     119           2 :           pool_put (frm->exporters, exp);
     120           2 :           return 0;
     121             :         }
     122             :     }
     123             : 
     124          46 :   collector_port = ntohs (mp_collector_port);
     125          46 :   if (collector_port == (u16) ~ 0)
     126           0 :     collector_port = UDP_DST_PORT_ipfix;
     127          46 :   ip_address_decode2 (mp_src_address, &src);
     128          46 :   fib_id = ntohl (mp_vrf_id);
     129             : 
     130          46 :   ip4_main_t *im = &ip4_main;
     131          46 :   if (fib_id == ~0)
     132             :     {
     133           0 :       fib_index = ~0;
     134             :     }
     135             :   else
     136             :     {
     137          46 :       uword *p = hash_get (im->fib_index_by_table_id, fib_id);
     138          46 :       if (!p)
     139           0 :         return VNET_API_ERROR_NO_SUCH_FIB;
     140          46 :       fib_index = p[0];
     141             :     }
     142             : 
     143          46 :   path_mtu = ntohl (mp_path_mtu);
     144          46 :   if (path_mtu == ~0)
     145           0 :     path_mtu = 512;             // RFC 7011 section 10.3.3.
     146          46 :   template_interval = ntohl (mp_template_interval);
     147          46 :   if (template_interval == ~0)
     148           0 :     template_interval = 20;
     149          46 :   udp_checksum = mp_udp_checksum;
     150             : 
     151             :   /*
     152             :    * If the collector address is set then the src must be too.
     153             :    * Collector address can be set to 0 to disable exporter
     154             :    */
     155          46 :   if (!ip_address_is_zero (&collector) && ip_address_is_zero (&src))
     156           0 :     return VNET_API_ERROR_INVALID_VALUE;
     157          46 :   if (collector.version != src.version)
     158           0 :     return VNET_API_ERROR_INVALID_VALUE;
     159             : 
     160          46 :   if (path_mtu > 1450 /* vpp does not support fragmentation */ )
     161           0 :     return VNET_API_ERROR_INVALID_VALUE;
     162             : 
     163          46 :   if (path_mtu < 68)
     164           0 :     return VNET_API_ERROR_INVALID_VALUE;
     165             : 
     166             :   /* Calculate how much header data we need. */
     167          46 :   if (collector.version == AF_IP4)
     168          46 :     ip_header_size = sizeof (ip4_header_t);
     169             :   else
     170           0 :     ip_header_size = sizeof (ip6_header_t);
     171          46 :   exp->all_headers_size = ip_header_size + sizeof (udp_header_t) +
     172          46 :                           sizeof (ipfix_message_header_t) +
     173             :                           sizeof (ipfix_set_header_t);
     174             : 
     175             :   /* Reset report streams if we are reconfiguring IP addresses */
     176          52 :   if (ip_address_cmp (&exp->ipfix_collector, &collector) ||
     177           6 :       ip_address_cmp (&exp->src_address, &src) ||
     178           6 :       exp->collector_port != collector_port)
     179          41 :     vnet_flow_reports_reset (exp);
     180             : 
     181          46 :   exp->ipfix_collector = collector;
     182          46 :   exp->collector_port = collector_port;
     183          46 :   exp->src_address = src;
     184          46 :   exp->fib_index = fib_index;
     185          46 :   exp->path_mtu = path_mtu;
     186          46 :   exp->template_interval = template_interval;
     187          46 :   exp->udp_checksum = udp_checksum;
     188             : 
     189             :   /* Turn on the flow reporting process */
     190          46 :   vlib_process_signal_event (vm, flow_report_process_node.index, 1, 0);
     191             : 
     192          46 :   return 0;
     193             : }
     194             : 
     195             : static void
     196          43 : vl_api_set_ipfix_exporter_t_handler (vl_api_set_ipfix_exporter_t *mp)
     197             : {
     198             :   vl_api_set_ipfix_exporter_reply_t *rmp;
     199          43 :   flow_report_main_t *frm = &flow_report_main;
     200          43 :   int rv = vl_api_set_ipfix_exporter_t_internal (
     201          43 :     mp->client_index, &mp->collector_address, mp->collector_port,
     202             :     &mp->src_address, mp->vrf_id, mp->path_mtu, mp->template_interval,
     203          43 :     mp->udp_checksum, USE_INDEX_0, 0);
     204             : 
     205          43 :   REPLY_MACRO (VL_API_SET_IPFIX_EXPORTER_REPLY);
     206             : }
     207             : 
     208             : static void
     209           5 : vl_api_ipfix_exporter_create_delete_t_handler (
     210             :   vl_api_ipfix_exporter_create_delete_t *mp)
     211             : {
     212             :   vl_api_ipfix_exporter_create_delete_reply_t *rmp;
     213           5 :   flow_report_main_t *frm = &flow_report_main;
     214           5 :   int rv = vl_api_set_ipfix_exporter_t_internal (
     215           5 :     mp->client_index, &mp->collector_address, mp->collector_port,
     216             :     &mp->src_address, mp->vrf_id, mp->path_mtu, mp->template_interval,
     217           5 :     mp->udp_checksum, USE_ANY_INDEX, mp->is_create);
     218             : 
     219           5 :   REPLY_MACRO (VL_API_IPFIX_EXPORTER_CREATE_DELETE_REPLY);
     220             : }
     221             : 
     222             : static void
     223           1 : vl_api_ipfix_exporter_dump_t_handler (vl_api_ipfix_exporter_dump_t * mp)
     224             : {
     225           1 :   flow_report_main_t *frm = &flow_report_main;
     226           1 :   ipfix_exporter_t *exp = pool_elt_at_index (flow_report_main.exporters, 0);
     227             :   vl_api_registration_t *reg;
     228             :   vl_api_ipfix_exporter_details_t *rmp;
     229           1 :   ip4_main_t *im = &ip4_main;
     230             :   u32 vrf_id;
     231             : 
     232           1 :   reg = vl_api_client_index_to_registration (mp->client_index);
     233           1 :   if (!reg)
     234           0 :     return;
     235             : 
     236           1 :   rmp = vl_msg_api_alloc (sizeof (*rmp));
     237           1 :   clib_memset (rmp, 0, sizeof (*rmp));
     238           1 :   rmp->_vl_msg_id =
     239           1 :     ntohs ((REPLY_MSG_ID_BASE) + VL_API_IPFIX_EXPORTER_DETAILS);
     240           1 :   rmp->context = mp->context;
     241             : 
     242           1 :   ip_address_encode2 (&exp->ipfix_collector, &rmp->collector_address);
     243           1 :   rmp->collector_port = htons (exp->collector_port);
     244           1 :   ip_address_encode2 (&exp->src_address, &rmp->src_address);
     245             : 
     246           1 :   if (exp->fib_index == ~0)
     247           0 :     vrf_id = ~0;
     248             :   else
     249           1 :     vrf_id = im->fibs[exp->fib_index].ft_table_id;
     250           1 :   rmp->vrf_id = htonl (vrf_id);
     251           1 :   rmp->path_mtu = htonl (exp->path_mtu);
     252           1 :   rmp->template_interval = htonl (exp->template_interval);
     253           1 :   rmp->udp_checksum = (exp->udp_checksum != 0);
     254             : 
     255           1 :   vl_api_send_msg (reg, (u8 *) rmp);
     256             : }
     257             : 
     258             : static void
     259          12 : ipfix_all_fill_details (vl_api_ipfix_all_exporter_details_t *rmp,
     260             :                         ipfix_exporter_t *exp)
     261             : {
     262          12 :   ip4_main_t *im = &ip4_main;
     263             :   u32 vrf_id;
     264             : 
     265          12 :   ip_address_encode2 (&exp->ipfix_collector, &rmp->collector_address);
     266          12 :   rmp->collector_port = htons (exp->collector_port);
     267          12 :   ip_address_encode2 (&exp->src_address, &rmp->src_address);
     268             : 
     269          12 :   if (exp->fib_index == ~0)
     270           0 :     vrf_id = ~0;
     271             :   else
     272          12 :     vrf_id = im->fibs[exp->fib_index].ft_table_id;
     273          12 :   rmp->vrf_id = htonl (vrf_id);
     274          12 :   rmp->path_mtu = htonl (exp->path_mtu);
     275          12 :   rmp->template_interval = htonl (exp->template_interval);
     276          12 :   rmp->udp_checksum = (exp->udp_checksum != 0);
     277          12 : }
     278             : 
     279             : static void
     280          12 : ipfix_all_exporter_details (flow_report_main_t *frm, u32 index,
     281             :                             vl_api_registration_t *rp, u32 context)
     282             : {
     283          12 :   ipfix_exporter_t *exp = pool_elt_at_index (frm->exporters, index);
     284             : 
     285             :   vl_api_ipfix_all_exporter_details_t *rmp;
     286             : 
     287          12 :   REPLY_MACRO_DETAILS4 (VL_API_IPFIX_ALL_EXPORTER_DETAILS, rp, context,
     288             :                         ({ ipfix_all_fill_details (rmp, exp); }));
     289          12 : }
     290             : 
     291             : static void
     292           6 : vl_api_ipfix_all_exporter_get_t_handler (vl_api_ipfix_all_exporter_get_t *mp)
     293             : {
     294           6 :   flow_report_main_t *frm = &flow_report_main;
     295             :   vl_api_ipfix_all_exporter_get_reply_t *rmp;
     296           6 :   int rv = 0;
     297             : 
     298          18 :   REPLY_AND_DETAILS_MACRO (
     299             :     VL_API_IPFIX_ALL_EXPORTER_GET_REPLY, frm->exporters,
     300             :     ({ ipfix_all_exporter_details (frm, cursor, rp, mp->context); }));
     301             : }
     302             : 
     303             : static void
     304           0 :   vl_api_set_ipfix_classify_stream_t_handler
     305             :   (vl_api_set_ipfix_classify_stream_t * mp)
     306             : {
     307             :   vl_api_set_ipfix_classify_stream_reply_t *rmp;
     308           0 :   flow_report_classify_main_t *fcm = &flow_report_classify_main;
     309           0 :   flow_report_main_t *frm = &flow_report_main;
     310           0 :   ipfix_exporter_t *exp = &frm->exporters[0];
     311           0 :   u32 domain_id = 0;
     312           0 :   u32 src_port = UDP_DST_PORT_ipfix;
     313           0 :   int rv = 0;
     314             : 
     315           0 :   domain_id = ntohl (mp->domain_id);
     316           0 :   src_port = ntohs (mp->src_port);
     317             : 
     318           0 :   if (fcm->src_port != 0 &&
     319           0 :       (fcm->domain_id != domain_id || fcm->src_port != (u16) src_port))
     320             :     {
     321           0 :       int rv = vnet_stream_change (exp, fcm->domain_id, fcm->src_port,
     322           0 :                                    domain_id, (u16) src_port);
     323           0 :       ASSERT (rv == 0);
     324             :     }
     325             : 
     326           0 :   fcm->domain_id = domain_id;
     327           0 :   fcm->src_port = (u16) src_port;
     328             : 
     329           0 :   REPLY_MACRO (VL_API_SET_IPFIX_CLASSIFY_STREAM_REPLY);
     330             : }
     331             : 
     332             : static void
     333           0 :   vl_api_ipfix_classify_stream_dump_t_handler
     334             :   (vl_api_ipfix_classify_stream_dump_t * mp)
     335             : {
     336           0 :   flow_report_classify_main_t *fcm = &flow_report_classify_main;
     337             :   vl_api_registration_t *reg;
     338             :   vl_api_ipfix_classify_stream_details_t *rmp;
     339             : 
     340           0 :   reg = vl_api_client_index_to_registration (mp->client_index);
     341           0 :   if (!reg)
     342           0 :     return;
     343             : 
     344           0 :   rmp = vl_msg_api_alloc (sizeof (*rmp));
     345           0 :   clib_memset (rmp, 0, sizeof (*rmp));
     346           0 :   rmp->_vl_msg_id = ntohs (VL_API_IPFIX_CLASSIFY_STREAM_DETAILS);
     347           0 :   rmp->context = mp->context;
     348           0 :   rmp->domain_id = htonl (fcm->domain_id);
     349           0 :   rmp->src_port = htons (fcm->src_port);
     350             : 
     351           0 :   vl_api_send_msg (reg, (u8 *) rmp);
     352             : }
     353             : 
     354             : static void
     355           0 :   vl_api_ipfix_classify_table_add_del_t_handler
     356             :   (vl_api_ipfix_classify_table_add_del_t * mp)
     357             : {
     358             :   vl_api_ipfix_classify_table_add_del_reply_t *rmp;
     359             :   vl_api_registration_t *reg;
     360           0 :   flow_report_classify_main_t *fcm = &flow_report_classify_main;
     361           0 :   flow_report_main_t *frm = &flow_report_main;
     362           0 :   ipfix_exporter_t *exp = &frm->exporters[0];
     363             :   vnet_flow_report_add_del_args_t args;
     364             :   ipfix_classify_table_t *table;
     365             :   int is_add;
     366             :   u32 classify_table_index;
     367             :   u8 ip_version;
     368             :   u8 transport_protocol;
     369           0 :   int rv = 0;
     370             : 
     371           0 :   reg = vl_api_client_index_to_registration (mp->client_index);
     372           0 :   if (!reg)
     373           0 :     return;
     374             : 
     375           0 :   classify_table_index = ntohl (mp->table_id);
     376           0 :   ip_version = (mp->ip_version == ADDRESS_IP4) ? 4 : 6;
     377           0 :   transport_protocol = mp->transport_protocol;
     378           0 :   is_add = mp->is_add;
     379             : 
     380           0 :   if (fcm->src_port == 0)
     381             :     {
     382             :       /* call set_ipfix_classify_stream first */
     383           0 :       rv = VNET_API_ERROR_UNSPECIFIED;
     384           0 :       goto out;
     385             :     }
     386             : 
     387           0 :   clib_memset (&args, 0, sizeof (args));
     388             : 
     389           0 :   table = 0;
     390             :   int i;
     391           0 :   for (i = 0; i < vec_len (fcm->tables); i++)
     392           0 :     if (ipfix_classify_table_index_valid (i))
     393           0 :       if (fcm->tables[i].classify_table_index == classify_table_index)
     394             :         {
     395           0 :           table = &fcm->tables[i];
     396           0 :           break;
     397             :         }
     398             : 
     399           0 :   if (is_add)
     400             :     {
     401           0 :       if (table)
     402             :         {
     403           0 :           rv = VNET_API_ERROR_VALUE_EXIST;
     404           0 :           goto out;
     405             :         }
     406           0 :       table = ipfix_classify_add_table ();
     407           0 :       table->classify_table_index = classify_table_index;
     408             :     }
     409             :   else
     410             :     {
     411           0 :       if (!table)
     412             :         {
     413           0 :           rv = VNET_API_ERROR_NO_SUCH_ENTRY;
     414           0 :           goto out;
     415             :         }
     416             :     }
     417             : 
     418           0 :   table->ip_version = ip_version;
     419           0 :   table->transport_protocol = transport_protocol;
     420             : 
     421           0 :   args.opaque.as_uword = table - fcm->tables;
     422           0 :   args.rewrite_callback = ipfix_classify_template_rewrite;
     423           0 :   args.flow_data_callback = ipfix_classify_send_flows;
     424           0 :   args.is_add = is_add;
     425           0 :   args.domain_id = fcm->domain_id;
     426           0 :   args.src_port = fcm->src_port;
     427             : 
     428           0 :   rv = vnet_flow_report_add_del (exp, &args, NULL);
     429             : 
     430             :   /* If deleting, or add failed */
     431           0 :   if (is_add == 0 || (rv && is_add))
     432           0 :     ipfix_classify_delete_table (table - fcm->tables);
     433             : 
     434           0 : out:
     435           0 :   REPLY_MACRO (VL_API_SET_IPFIX_CLASSIFY_STREAM_REPLY);
     436             : }
     437             : 
     438             : static void
     439           0 : send_ipfix_classify_table_details (u32 table_index,
     440             :                                    vl_api_registration_t * reg, u32 context)
     441             : {
     442           0 :   flow_report_classify_main_t *fcm = &flow_report_classify_main;
     443             :   vl_api_ipfix_classify_table_details_t *mp;
     444             : 
     445           0 :   ipfix_classify_table_t *table = &fcm->tables[table_index];
     446             : 
     447           0 :   mp = vl_msg_api_alloc (sizeof (*mp));
     448           0 :   clib_memset (mp, 0, sizeof (*mp));
     449           0 :   mp->_vl_msg_id = ntohs (VL_API_IPFIX_CLASSIFY_TABLE_DETAILS);
     450           0 :   mp->context = context;
     451           0 :   mp->table_id = htonl (table->classify_table_index);
     452           0 :   mp->ip_version = (table->ip_version == 4) ? ADDRESS_IP4 : ADDRESS_IP6;
     453           0 :   mp->transport_protocol = table->transport_protocol;
     454             : 
     455           0 :   vl_api_send_msg (reg, (u8 *) mp);
     456           0 : }
     457             : 
     458             : static void
     459           0 :   vl_api_ipfix_classify_table_dump_t_handler
     460             :   (vl_api_ipfix_classify_table_dump_t * mp)
     461             : {
     462           0 :   flow_report_classify_main_t *fcm = &flow_report_classify_main;
     463             :   vl_api_registration_t *reg;
     464             :   u32 i;
     465             : 
     466           0 :   reg = vl_api_client_index_to_registration (mp->client_index);
     467           0 :   if (!reg)
     468           0 :     return;
     469             : 
     470           0 :   for (i = 0; i < vec_len (fcm->tables); i++)
     471           0 :     if (ipfix_classify_table_index_valid (i))
     472           0 :       send_ipfix_classify_table_details (i, reg, mp->context);
     473             : }
     474             : 
     475             : static void
     476          33 : vl_api_ipfix_flush_t_handler (vl_api_ipfix_flush_t * mp)
     477             : {
     478          33 :   flow_report_main_t *frm = &flow_report_main;
     479             :   vl_api_ipfix_flush_reply_t *rmp;
     480             :   vl_api_registration_t *reg;
     481          33 :   vlib_main_t *vm = vlib_get_main ();
     482          33 :   int rv = 0;
     483             : 
     484          33 :   reg = vl_api_client_index_to_registration (mp->client_index);
     485          33 :   if (!reg)
     486           0 :     return;
     487             : 
     488             :   /* poke the flow reporting process */
     489          33 :   vlib_process_signal_event (vm, flow_report_process_node.index,
     490             :                              1 /* type_opaque */ , 0 /* data */ );
     491             : 
     492          33 :   REPLY_MACRO (VL_API_IPFIX_FLUSH_REPLY);
     493             : }
     494             : 
     495             : #include <vnet/ipfix-export/ipfix_export.api.c>
     496             : static clib_error_t *
     497         559 : flow_api_hookup (vlib_main_t * vm)
     498             : {
     499         559 :   flow_report_main_t *frm = &flow_report_main;
     500             :   /*
     501             :    * Set up the (msg_name, crc, message-id) table
     502             :    */
     503         559 :   REPLY_MSG_ID_BASE = setup_message_id_table ();
     504             : 
     505         559 :   return 0;
     506             : }
     507             : 
     508       12879 : VLIB_API_INIT_FUNCTION (flow_api_hookup);
     509             : 
     510             : /*
     511             :  * fd.io coding-style-patch-verification: ON
     512             :  *
     513             :  * Local Variables:
     514             :  * eval: (c-set-style "gnu")
     515             :  * End:
     516             :  */

Generated by: LCOV version 1.14