LCOV - code coverage report
Current view: top level - vnet/ipfix-export - flow_report.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 231 440 52.5 %
Date: 2023-07-05 22:20:52 Functions: 20 27 74.1 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2015 Cisco and/or its affiliates.
       3             :  * Licensed under the Apache License, Version 2.0 (the "License");
       4             :  * you may not use this file except in compliance with the License.
       5             :  * You may obtain a copy of the License at:
       6             :  *
       7             :  *     http://www.apache.org/licenses/LICENSE-2.0
       8             :  *
       9             :  * Unless required by applicable law or agreed to in writing, software
      10             :  * distributed under the License is distributed on an "AS IS" BASIS,
      11             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             :  * See the License for the specific language governing permissions and
      13             :  * limitations under the License.
      14             :  */
      15             : /*
      16             :  * flow_report.c
      17             :  */
      18             : #include <vppinfra/atomics.h>
      19             : #include <vnet/ipfix-export/flow_report.h>
      20             : #include <vnet/api_errno.h>
      21             : #include <vnet/udp/udp.h>
      22             : 
      23             : flow_report_main_t flow_report_main;
      24             : 
      25             : static_always_inline u8
      26         254 : stream_index_valid (ipfix_exporter_t *exp, u32 index)
      27             : {
      28         254 :   return index < vec_len (exp->streams) && exp->streams[index].domain_id != ~0;
      29             : }
      30             : 
      31             : static_always_inline flow_report_stream_t *
      32          39 : add_stream (ipfix_exporter_t *exp)
      33             : {
      34             :   u32 i;
      35          39 :   for (i = 0; i < vec_len (exp->streams); i++)
      36          33 :     if (!stream_index_valid (exp, i))
      37          33 :       return &exp->streams[i];
      38           6 :   u32 index = vec_len (exp->streams);
      39           6 :   vec_validate (exp->streams, index);
      40           6 :   return &exp->streams[index];
      41             : }
      42             : 
      43             : static_always_inline void
      44          37 : delete_stream (ipfix_exporter_t *exp, u32 index)
      45             : {
      46          37 :   ASSERT (index < vec_len (exp->streams));
      47          37 :   ASSERT (exp->streams[index].domain_id != ~0);
      48          37 :   exp->streams[index].domain_id = ~0;
      49          37 : }
      50             : 
      51             : static i32
      52         161 : find_stream (ipfix_exporter_t *exp, u32 domain_id, u16 src_port)
      53             : {
      54             :   flow_report_stream_t *stream;
      55             :   u32 i;
      56         194 :   for (i = 0; i < vec_len (exp->streams); i++)
      57         155 :     if (stream_index_valid (exp, i))
      58             :       {
      59         122 :         stream = &exp->streams[i];
      60         122 :         if (domain_id == stream->domain_id)
      61             :           {
      62         122 :             if (src_port != stream->src_port)
      63           0 :               return -2;
      64         122 :             return i;
      65             :           }
      66           0 :         else if (src_port == stream->src_port)
      67             :           {
      68           0 :             return -2;
      69             :           }
      70             :       }
      71          39 :   return -1;
      72             : }
      73             : 
      74             : int
      75         171 : send_template_packet (flow_report_main_t *frm, ipfix_exporter_t *exp,
      76             :                       flow_report_t *fr, u32 *buffer_indexp)
      77             : {
      78             :   u32 bi0;
      79             :   vlib_buffer_t *b0;
      80             :   ip4_ipfix_template_packet_t *tp4;
      81             :   ip6_ipfix_template_packet_t *tp6;
      82             :   ipfix_message_header_t *h;
      83             :   ip4_header_t *ip4;
      84             :   ip6_header_t *ip6;
      85             :   void *ip;
      86             :   udp_header_t *udp;
      87         171 :   vlib_main_t *vm = frm->vlib_main;
      88             :   flow_report_stream_t *stream;
      89             : 
      90         171 :   ASSERT (buffer_indexp);
      91             : 
      92         171 :   if (fr->update_rewrite || fr->rewrite == 0)
      93             :     {
      94         230 :       if (ip_address_is_zero (&exp->ipfix_collector) ||
      95          83 :           ip_address_is_zero (&exp->src_address))
      96             :         {
      97          64 :           vlib_node_set_state (frm->vlib_main, flow_report_process_node.index,
      98             :                                VLIB_NODE_STATE_DISABLED);
      99          64 :           return -1;
     100             :         }
     101          83 :       vec_free (fr->rewrite);
     102          83 :       fr->update_rewrite = 1;
     103             :     }
     104             : 
     105         107 :   if (fr->update_rewrite)
     106             :     {
     107         166 :       fr->rewrite = fr->rewrite_callback (
     108          83 :         exp, fr, exp->collector_port, fr->report_elements,
     109             :         fr->n_report_elements, fr->stream_indexp);
     110          83 :       fr->update_rewrite = 0;
     111             :     }
     112             : 
     113         107 :   if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
     114           0 :     return -1;
     115             : 
     116         107 :   b0 = vlib_get_buffer (vm, bi0);
     117             : 
     118         107 :   ASSERT (vec_len (fr->rewrite) < vlib_buffer_get_default_data_size (vm));
     119             : 
     120         107 :   clib_memcpy_fast (b0->data, fr->rewrite, vec_len (fr->rewrite));
     121         107 :   b0->current_data = 0;
     122         107 :   b0->current_length = vec_len (fr->rewrite);
     123         107 :   b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_FLOW_REPORT);
     124         107 :   vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
     125         107 :   vnet_buffer (b0)->sw_if_index[VLIB_TX] = exp->fib_index;
     126             : 
     127         107 :   if (ip_addr_version (&exp->ipfix_collector) == AF_IP4)
     128             :     {
     129         107 :       tp4 = vlib_buffer_get_current (b0);
     130         107 :       ip4 = (ip4_header_t *) &tp4->ip4;
     131         107 :       ip = ip4;
     132         107 :       udp = (udp_header_t *) (ip4 + 1);
     133             :     }
     134             :   else
     135             :     {
     136           0 :       tp6 = vlib_buffer_get_current (b0);
     137           0 :       ip6 = (ip6_header_t *) &tp6->ip6;
     138           0 :       ip = ip6;
     139           0 :       udp = (udp_header_t *) (ip6 + 1);
     140             :     }
     141         107 :   h = (ipfix_message_header_t *) (udp + 1);
     142             : 
     143             :   /* FIXUP: message header export_time */
     144         107 :   h->export_time = (u32)
     145         214 :     (((f64) frm->unix_time_0) +
     146         107 :      (vlib_time_now (frm->vlib_main) - frm->vlib_time_0));
     147         107 :   h->export_time = clib_host_to_net_u32 (h->export_time);
     148             : 
     149         107 :   stream = &exp->streams[fr->stream_index];
     150             : 
     151             :   /* FIXUP: message header sequence_number. Templates do not increase it */
     152         107 :   h->sequence_number = clib_host_to_net_u32 (stream->sequence_number);
     153             : 
     154             :   /* FIXUP: udp length */
     155         107 :   if (ip_addr_version (&exp->ipfix_collector) == AF_IP4)
     156         107 :     udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip4));
     157             :   else
     158           0 :     udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip6));
     159             : 
     160         107 :   if (exp->udp_checksum || ip_addr_version (&exp->ipfix_collector) == AF_IP6)
     161             :     {
     162             :       /* RFC 7011 section 10.3.2. */
     163             : 
     164           0 :       if (ip_addr_version (&exp->ipfix_collector) == AF_IP4)
     165           0 :         udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
     166             :       else
     167             :         {
     168           0 :           int bogus = 0;
     169           0 :           udp->checksum =
     170           0 :             ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip, &bogus);
     171             :         }
     172             : 
     173           0 :       if (udp->checksum == 0)
     174           0 :         udp->checksum = 0xffff;
     175             :     }
     176             : 
     177         107 :   *buffer_indexp = bi0;
     178             : 
     179         107 :   fr->last_template_sent = vlib_time_now (vm);
     180             : 
     181         107 :   return 0;
     182             : }
     183             : 
     184             : u32 always_inline
     185           0 : ipfix_write_headers (ipfix_exporter_t *exp, void *data, void **ip,
     186             :                      udp_header_t **udp, u32 len)
     187             : {
     188           0 :   if (ip_addr_version (&exp->ipfix_collector) == AF_IP4)
     189             :     {
     190             :       ip4_ipfix_template_packet_t *tp4;
     191             :       ip4_header_t *ip4;
     192             : 
     193           0 :       tp4 = (ip4_ipfix_template_packet_t *) data;
     194           0 :       ip4 = (ip4_header_t *) &tp4->ip4;
     195           0 :       ip4->ip_version_and_header_length = 0x45;
     196           0 :       ip4->ttl = 254;
     197           0 :       ip4->protocol = IP_PROTOCOL_UDP;
     198           0 :       ip4->flags_and_fragment_offset = 0;
     199           0 :       ip4->src_address.as_u32 = exp->src_address.ip.ip4.as_u32;
     200           0 :       ip4->dst_address.as_u32 = exp->ipfix_collector.ip.ip4.as_u32;
     201           0 :       *ip = ip4;
     202           0 :       *udp = (udp_header_t *) (ip4 + 1);
     203             : 
     204           0 :       (*udp)->length = clib_host_to_net_u16 (len - sizeof (*ip4));
     205           0 :       return sizeof (*ip4);
     206             :     }
     207             :   else
     208             :     {
     209             :       ip6_ipfix_template_packet_t *tp6;
     210             :       ip6_header_t *ip6;
     211             : 
     212           0 :       tp6 = (ip6_ipfix_template_packet_t *) data;
     213           0 :       ip6 = (ip6_header_t *) &tp6->ip6;
     214           0 :       ip6->ip_version_traffic_class_and_flow_label =
     215           0 :         clib_host_to_net_u32 (6 << 28);
     216           0 :       ip6->hop_limit = 254;
     217           0 :       ip6->protocol = IP_PROTOCOL_UDP;
     218           0 :       ip6->src_address = exp->src_address.ip.ip6;
     219           0 :       ip6->dst_address = exp->ipfix_collector.ip.ip6;
     220           0 :       *ip = ip6;
     221           0 :       *udp = (udp_header_t *) (ip6 + 1);
     222           0 :       (*udp)->length = clib_host_to_net_u16 (len - sizeof (*ip6));
     223           0 :       return sizeof (*ip6);
     224             :     }
     225             : }
     226             : 
     227             : u8 *
     228           0 : vnet_flow_rewrite_generic_callback (ipfix_exporter_t *exp, flow_report_t *fr,
     229             :                                     u16 collector_port,
     230             :                                     ipfix_report_element_t *report_elts,
     231             :                                     u32 n_elts, u32 *stream_indexp)
     232             : {
     233             :   ip4_header_t *ip4;
     234             :   ip6_header_t *ip6;
     235             :   void *ip;
     236             :   udp_header_t *udp;
     237             :   ipfix_message_header_t *h;
     238             :   ipfix_set_header_t *s;
     239             :   ipfix_template_header_t *t;
     240             :   ipfix_field_specifier_t *f;
     241             :   ipfix_field_specifier_t *first_field;
     242           0 :   u8 *rewrite = 0;
     243             :   flow_report_stream_t *stream;
     244             :   int i;
     245             :   ipfix_report_element_t *ep;
     246             :   u32 size;
     247             : 
     248           0 :   ASSERT (stream_indexp);
     249           0 :   ASSERT (n_elts);
     250           0 :   ASSERT (report_elts);
     251             : 
     252           0 :   stream = &exp->streams[fr->stream_index];
     253           0 :   *stream_indexp = fr->stream_index;
     254             : 
     255           0 :   if (ip_addr_version (&exp->ipfix_collector) == AF_IP4)
     256           0 :     size = sizeof (ip4_ipfix_template_packet_t);
     257             :   else
     258           0 :     size = sizeof (ip6_ipfix_template_packet_t);
     259             :   /* allocate rewrite space */
     260           0 :   vec_validate_aligned (rewrite,
     261             :                         size + n_elts * sizeof (ipfix_field_specifier_t) - 1,
     262             :                         CLIB_CACHE_LINE_BYTES);
     263             : 
     264             :   /* create the packet rewrite string */
     265           0 :   ipfix_write_headers (exp, rewrite, &ip, &udp, vec_len (rewrite));
     266             : 
     267           0 :   h = (ipfix_message_header_t *) (udp + 1);
     268           0 :   s = (ipfix_set_header_t *) (h + 1);
     269           0 :   t = (ipfix_template_header_t *) (s + 1);
     270           0 :   first_field = f = (ipfix_field_specifier_t *) (t + 1);
     271           0 :   udp->src_port = clib_host_to_net_u16 (stream->src_port);
     272           0 :   udp->dst_port = clib_host_to_net_u16 (collector_port);
     273             : 
     274             :   /* FIXUP LATER: message header export_time */
     275           0 :   h->domain_id = clib_host_to_net_u32 (stream->domain_id);
     276             : 
     277           0 :   ep = report_elts;
     278             : 
     279           0 :   for (i = 0; i < n_elts; i++)
     280             :     {
     281           0 :       f->e_id_length = ipfix_e_id_length (0, ep->info_element, ep->size);
     282           0 :       f++;
     283           0 :       ep++;
     284             :     }
     285             : 
     286           0 :   ASSERT (f - first_field);
     287             :   /* Field count in this template */
     288           0 :   t->id_count = ipfix_id_count (fr->template_id, f - first_field);
     289             : 
     290             :   /* set length in octets */
     291           0 :   s->set_id_length =
     292           0 :     ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
     293             : 
     294             :   /* message length in octets */
     295           0 :   h->version_length = version_length ((u8 *) f - (u8 *) h);
     296             : 
     297           0 :   if (ip_addr_version (&exp->ipfix_collector) == AF_IP4)
     298             :     {
     299           0 :       ip4 = (ip4_header_t *) ip;
     300           0 :       ip4->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip4);
     301           0 :       ip4->checksum = ip4_header_checksum (ip4);
     302             :     }
     303             :   else
     304             :     {
     305           0 :       ip6 = (ip6_header_t *) ip;
     306             :       /* IPv6 payload length does not include the IPv6 header */
     307           0 :       ip6->payload_length = clib_host_to_net_u16 ((u8 *) f - (u8 *) udp);
     308             :     }
     309             : 
     310           0 :   return rewrite;
     311             : }
     312             : 
     313             : vlib_buffer_t *
     314           0 : vnet_ipfix_exp_get_buffer (vlib_main_t *vm, ipfix_exporter_t *exp,
     315             :                            flow_report_t *fr, u32 thread_index)
     316             : {
     317             :   u32 bi0;
     318             :   vlib_buffer_t *b0;
     319             : 
     320           0 :   if (fr->per_thread_data[thread_index].buffer)
     321           0 :     return fr->per_thread_data[thread_index].buffer;
     322             : 
     323           0 :   if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
     324           0 :     return NULL;
     325             : 
     326             :   /* Initialize the buffer */
     327           0 :   b0 = fr->per_thread_data[thread_index].buffer = vlib_get_buffer (vm, bi0);
     328             : 
     329           0 :   b0->current_data = 0;
     330           0 :   b0->current_length = exp->all_headers_size;
     331           0 :   b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_FLOW_REPORT);
     332           0 :   vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
     333           0 :   vnet_buffer (b0)->sw_if_index[VLIB_TX] = exp->fib_index;
     334           0 :   fr->per_thread_data[thread_index].next_data_offset = b0->current_length;
     335             : 
     336           0 :   return b0;
     337             : }
     338             : 
     339             : /*
     340             :  * Send a buffer that is mostly populated. Has flow records but needs some
     341             :  * header fields updated.
     342             :  */
     343             : void
     344           0 : vnet_ipfix_exp_send_buffer (vlib_main_t *vm, ipfix_exporter_t *exp,
     345             :                             flow_report_t *fr, flow_report_stream_t *stream,
     346             :                             u32 thread_index, vlib_buffer_t *b0)
     347             : {
     348           0 :   flow_report_main_t *frm = &flow_report_main;
     349             :   vlib_frame_t *f;
     350             :   ipfix_set_header_t *s;
     351             :   ipfix_message_header_t *h;
     352           0 :   ip4_header_t *ip4 = 0;
     353           0 :   ip6_header_t *ip6 = 0;
     354             :   void *ip;
     355             :   udp_header_t *udp;
     356             :   int ip_len;
     357             : 
     358             :   /* nothing to send */
     359           0 :   if (fr->per_thread_data[thread_index].next_data_offset <=
     360           0 :       exp->all_headers_size)
     361           0 :     return;
     362             : 
     363           0 :   ip_len = ipfix_write_headers (exp, (void *) vlib_buffer_get_current (b0),
     364           0 :                                 &ip, &udp, b0->current_length);
     365             : 
     366           0 :   h = (ipfix_message_header_t *) (udp + 1);
     367           0 :   s = (ipfix_set_header_t *) (h + 1);
     368             : 
     369           0 :   udp->src_port = clib_host_to_net_u16 (stream->src_port);
     370           0 :   udp->dst_port = clib_host_to_net_u16 (exp->collector_port);
     371           0 :   udp->checksum = 0;
     372             : 
     373             :   /* FIXUP: message header export_time */
     374           0 :   h->export_time =
     375           0 :     (u32) (((f64) frm->unix_time_0) + (vlib_time_now (vm) - frm->vlib_time_0));
     376           0 :   h->export_time = clib_host_to_net_u32 (h->export_time);
     377           0 :   h->domain_id = clib_host_to_net_u32 (stream->domain_id);
     378             : 
     379             :   /*
     380             :    * RFC 7011: Section 3.2
     381             :    *
     382             :    * Incremental sequence counter modulo 2^32 of all IPFIX Data Records
     383             :    * sent in the current stream from the current Observation Domain by
     384             :    * the Exporting Process
     385             :    */
     386           0 :   h->sequence_number =
     387           0 :     clib_atomic_fetch_add (&stream->sequence_number,
     388             :                            fr->per_thread_data[thread_index].n_data_records);
     389           0 :   h->sequence_number = clib_host_to_net_u32 (h->sequence_number);
     390             : 
     391             :   /*
     392             :    * For data records we use the template ID as the set ID.
     393             :    * RFC 7011: 3.4.3
     394             :    */
     395           0 :   s->set_id_length = ipfix_set_id_length (
     396           0 :     fr->template_id,
     397           0 :     b0->current_length - (ip_len + sizeof (*udp) + sizeof (*h)));
     398           0 :   h->version_length =
     399           0 :     version_length (b0->current_length - (ip_len + sizeof (*udp)));
     400             : 
     401           0 :   if (ip_addr_version (&exp->ipfix_collector) == AF_IP4)
     402             :     {
     403           0 :       ip4 = (ip4_header_t *) ip;
     404           0 :       ip4->length = clib_host_to_net_u16 (b0->current_length);
     405           0 :       ip4->checksum = ip4_header_checksum (ip4);
     406           0 :       udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip4));
     407           0 :       ASSERT (ip4_header_checksum_is_valid (ip4));
     408             :     }
     409             :   else
     410             :     {
     411           0 :       ip6 = (ip6_header_t *) ip;
     412             :       /* Ipv6 payload length does not include the IPv6 header */
     413           0 :       ip6->payload_length =
     414           0 :         clib_host_to_net_u16 (b0->current_length - sizeof (*ip6));
     415           0 :       udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip6));
     416             :     }
     417             : 
     418           0 :   if (exp->udp_checksum || ip_addr_version (&exp->ipfix_collector) == AF_IP6)
     419             :     {
     420             :       /* RFC 7011 section 10.3.2. */
     421           0 :       if (ip_addr_version (&exp->ipfix_collector) == AF_IP4)
     422           0 :         udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip4);
     423             :       else
     424             :         {
     425           0 :           int bogus = 0;
     426           0 :           udp->checksum =
     427           0 :             ip6_tcp_udp_icmp_compute_checksum (vm, b0, ip6, &bogus);
     428             :         }
     429           0 :       if (udp->checksum == 0)
     430           0 :         udp->checksum = 0xffff;
     431             :     }
     432             : 
     433             :   /* Find or allocate a frame */
     434           0 :   f = fr->per_thread_data[thread_index].frame;
     435           0 :   if (PREDICT_FALSE (f == 0))
     436             :     {
     437             :       u32 *to_next;
     438           0 :       if (ip_addr_version (&exp->ipfix_collector) == AF_IP4)
     439           0 :         f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
     440             :       else
     441           0 :         f = vlib_get_frame_to_node (vm, ip6_lookup_node.index);
     442           0 :       fr->per_thread_data[thread_index].frame = f;
     443           0 :       u32 bi0 = vlib_get_buffer_index (vm, b0);
     444             : 
     445             :       /* Enqueue the buffer */
     446           0 :       to_next = vlib_frame_vector_args (f);
     447           0 :       to_next[0] = bi0;
     448           0 :       f->n_vectors = 1;
     449             :     }
     450             : 
     451           0 :   if (ip_addr_version (&exp->ipfix_collector) == AF_IP4)
     452           0 :     vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
     453             :   else
     454           0 :     vlib_put_frame_to_node (vm, ip6_lookup_node.index, f);
     455             : 
     456           0 :   fr->per_thread_data[thread_index].frame = NULL;
     457           0 :   fr->per_thread_data[thread_index].buffer = NULL;
     458           0 :   fr->per_thread_data[thread_index].next_data_offset = 0;
     459             : }
     460             : 
     461             : static void
     462         200 : flow_report_process_send (vlib_main_t *vm, flow_report_main_t *frm,
     463             :                           ipfix_exporter_t *exp, flow_report_t *fr,
     464             :                           u32 next_node, u32 template_bi)
     465             : {
     466         200 :   vlib_frame_t *nf = 0;
     467             :   u32 *to_next;
     468             : 
     469         200 :   nf = vlib_get_frame_to_node (vm, next_node);
     470         200 :   nf->n_vectors = 0;
     471         200 :   to_next = vlib_frame_vector_args (nf);
     472             : 
     473         200 :   if (template_bi != ~0)
     474             :     {
     475         107 :       to_next[0] = template_bi;
     476         107 :       to_next++;
     477         107 :       nf->n_vectors++;
     478             :     }
     479             : 
     480         200 :   nf = fr->flow_data_callback (frm, exp, fr, nf, to_next, next_node);
     481         200 :   if (nf)
     482             :     {
     483         200 :       if (nf->n_vectors)
     484         107 :         vlib_put_frame_to_node (vm, next_node, nf);
     485             :       else
     486             :         {
     487          93 :           vlib_frame_free (vm, nf);
     488             :         }
     489             :     }
     490         200 : }
     491             : 
     492             : static uword
     493         559 : flow_report_process (vlib_main_t * vm,
     494             :                      vlib_node_runtime_t * rt, vlib_frame_t * f)
     495             : {
     496         559 :   flow_report_main_t *frm = &flow_report_main;
     497             :   flow_report_t *fr;
     498             :   u32 ip4_lookup_node_index;
     499             :   vlib_node_t *ip4_lookup_node;
     500             :   u32 ip6_lookup_node_index;
     501             :   vlib_node_t *ip6_lookup_node;
     502             :   u32 template_bi;
     503             :   int send_template;
     504             :   f64 now, wait_time;
     505         559 :   f64 def_wait_time = 5.0;
     506             :   int rv;
     507             :   uword event_type;
     508         559 :   uword *event_data = 0;
     509             : 
     510             :   /* Wait for Godot... */
     511         559 :   vlib_process_wait_for_event_or_clock (vm, 1e9);
     512           7 :   event_type = vlib_process_get_events (vm, &event_data);
     513           7 :   if (event_type != 1)
     514           0 :     clib_warning ("bogus kickoff event received, %d", event_type);
     515           7 :   vec_reset_length (event_data);
     516             : 
     517             :   /* Enqueue pkts to ip4-lookup */
     518           7 :   ip4_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip4-lookup");
     519           7 :   ip4_lookup_node_index = ip4_lookup_node->index;
     520             : 
     521             :   /* Enqueue pkts to ip6-lookup */
     522           7 :   ip6_lookup_node = vlib_get_node_by_name (vm, (u8 *) "ip6-lookup");
     523           7 :   ip6_lookup_node_index = ip6_lookup_node->index;
     524             : 
     525           7 :   wait_time = def_wait_time;
     526             : 
     527             :   while (1)
     528         201 :     {
     529         208 :       vlib_process_wait_for_event_or_clock (vm, wait_time);
     530         201 :       event_type = vlib_process_get_events (vm, &event_data);
     531         201 :       vec_reset_length (event_data);
     532             :       ipfix_exporter_t *exp;
     533         407 :       pool_foreach (exp, frm->exporters)
     534             :         {
     535             : 
     536             :           /* 5s delay by default, possibly reduced by template intervals */
     537         206 :           wait_time = def_wait_time;
     538             : 
     539         470 :           vec_foreach (fr, exp->reports)
     540             :             {
     541             :               f64 next_template;
     542         264 :               now = vlib_time_now (vm);
     543             : 
     544             :               /* Need to send a template packet? */
     545         264 :               send_template =
     546         264 :                 now > (fr->last_template_sent + exp->template_interval);
     547         264 :               send_template += fr->last_template_sent == 0;
     548         264 :               template_bi = ~0;
     549         264 :               rv = 0;
     550             : 
     551         264 :               if (send_template)
     552         171 :                 rv = send_template_packet (frm, exp, fr, &template_bi);
     553             : 
     554         264 :               if (rv < 0)
     555          64 :                 continue;
     556             : 
     557             :               /*
     558             :                * decide if template should be sent sooner than current wait
     559             :                * time
     560             :                */
     561         200 :               next_template =
     562         200 :                 (fr->last_template_sent + exp->template_interval) - now;
     563         200 :               wait_time = clib_min (wait_time, next_template);
     564             : 
     565         200 :               if (ip_addr_version (&exp->ipfix_collector) == AF_IP4)
     566             :                 {
     567         200 :                   flow_report_process_send (
     568             :                     vm, frm, exp, fr, ip4_lookup_node_index, template_bi);
     569             :                 }
     570             :               else
     571             :                 {
     572           0 :                   flow_report_process_send (
     573             :                     vm, frm, exp, fr, ip6_lookup_node_index, template_bi);
     574             :                 }
     575             :             }
     576             :         }
     577             :     }
     578             : 
     579             :   return 0;                     /* not so much */
     580             : }
     581             : 
     582             : /* *INDENT-OFF* */
     583      178120 : VLIB_REGISTER_NODE (flow_report_process_node) = {
     584             :     .function = flow_report_process,
     585             :     .type = VLIB_NODE_TYPE_PROCESS,
     586             :     .name = "flow-report-process",
     587             : };
     588             : /* *INDENT-ON* */
     589             : 
     590             : int
     591         161 : vnet_flow_report_add_del (ipfix_exporter_t *exp,
     592             :                           vnet_flow_report_add_del_args_t *a, u16 *template_id)
     593             : {
     594             :   int i;
     595         161 :   int found_index = ~0;
     596             :   flow_report_t *fr;
     597             :   flow_report_stream_t *stream;
     598             :   u32 si;
     599         161 :   vlib_thread_main_t *tm = &vlib_thread_main;
     600         161 :   flow_report_main_t *frm = &flow_report_main;
     601         161 :   vlib_main_t *vm = frm->vlib_main;
     602             :   int size;
     603             : 
     604         161 :   si = find_stream (exp, a->domain_id, a->src_port);
     605         161 :   if (si == -2)
     606           0 :     return VNET_API_ERROR_INVALID_VALUE;
     607         161 :   if (si == -1 && a->is_add == 0)
     608           0 :     return VNET_API_ERROR_NO_SUCH_ENTRY;
     609             : 
     610         262 :   for (i = 0; i < vec_len (exp->reports); i++)
     611             :     {
     612         177 :       fr = vec_elt_at_index (exp->reports, i);
     613         177 :       if (fr->opaque.as_uword == a->opaque.as_uword
     614         177 :           && fr->rewrite_callback == a->rewrite_callback
     615          76 :           && fr->flow_data_callback == a->flow_data_callback)
     616             :         {
     617          76 :           found_index = i;
     618          76 :           if (template_id)
     619          58 :             *template_id = fr->template_id;
     620          76 :           break;
     621             :         }
     622             :     }
     623             : 
     624         161 :   if (a->is_add == 0)
     625             :     {
     626          76 :       if (found_index != ~0)
     627             :         {
     628          76 :           for (int i = 0;
     629         152 :                i < vec_len (exp->reports[found_index].per_thread_data); i++)
     630             :             {
     631             :               u32 bi;
     632          76 :               if (exp->reports[found_index].per_thread_data[i].buffer)
     633             :                 {
     634           0 :                   bi = vlib_get_buffer_index (
     635           0 :                     vm, exp->reports[found_index].per_thread_data[i].buffer);
     636           0 :                   vlib_buffer_free (vm, &bi, 1);
     637             :                 }
     638             :             }
     639          76 :           vec_free (exp->reports[found_index].per_thread_data);
     640             : 
     641          76 :           vec_delete (exp->reports, 1, found_index);
     642          76 :           stream = &exp->streams[si];
     643          76 :           stream->n_reports--;
     644          76 :           if (stream->n_reports == 0)
     645          37 :             delete_stream (exp, si);
     646          76 :           return 0;
     647             :         }
     648           0 :       return VNET_API_ERROR_NO_SUCH_ENTRY;
     649             :     }
     650             : 
     651          85 :   if (found_index != ~0)
     652           0 :     return VNET_API_ERROR_VALUE_EXIST;
     653             : 
     654          85 :   if (si == -1)
     655             :     {
     656          39 :       stream = add_stream (exp);
     657          39 :       stream->domain_id = a->domain_id;
     658          39 :       stream->src_port = a->src_port;
     659          39 :       stream->sequence_number = 0;
     660          39 :       stream->n_reports = 0;
     661          39 :       si = stream - exp->streams;
     662             :     }
     663             :   else
     664          46 :     stream = &exp->streams[si];
     665             : 
     666          85 :   stream->n_reports++;
     667             : 
     668          85 :   vec_add2 (exp->reports, fr, 1);
     669             : 
     670          85 :   fr->stream_index = si;
     671          85 :   fr->template_id = 256 + stream->next_template_no;
     672          85 :   stream->next_template_no = (stream->next_template_no + 1) % (65536 - 256);
     673          85 :   fr->update_rewrite = 1;
     674          85 :   fr->opaque = a->opaque;
     675          85 :   fr->rewrite_callback = a->rewrite_callback;
     676          85 :   fr->flow_data_callback = a->flow_data_callback;
     677          85 :   fr->report_elements = a->report_elements;
     678          85 :   fr->n_report_elements = a->n_report_elements;
     679          85 :   fr->stream_indexp = a->stream_indexp;
     680          85 :   vec_validate (fr->per_thread_data, tm->n_threads);
     681             :   /* Store the flow_report index back in the args struct */
     682          85 :   a->flow_report_index = fr - exp->reports;
     683             : 
     684          85 :   size = 0;
     685          85 :   for (int i = 0; i < fr->n_report_elements; i++)
     686           0 :     size += fr->report_elements[i].size;
     687          85 :   fr->data_record_size = size;
     688          85 :   if (template_id)
     689          61 :     *template_id = fr->template_id;
     690             : 
     691          85 :   return 0;
     692             : }
     693             : 
     694             : clib_error_t *
     695           0 : flow_report_add_del_error_to_clib_error (int error)
     696             : {
     697           0 :   switch (error)
     698             :     {
     699           0 :     case 0:
     700           0 :       return 0;
     701           0 :     case VNET_API_ERROR_NO_SUCH_ENTRY:
     702           0 :       return clib_error_return (0, "Flow report not found");
     703           0 :     case VNET_API_ERROR_VALUE_EXIST:
     704           0 :       return clib_error_return (0, "Flow report already exists");
     705           0 :     case VNET_API_ERROR_INVALID_VALUE:
     706           0 :       return clib_error_return (0, "Expecting either still unused values "
     707             :                                 "for both domain_id and src_port "
     708             :                                 "or already used values for both fields");
     709           0 :     default:
     710           0 :       return clib_error_return (0, "vnet_flow_report_add_del returned %d",
     711             :                                 error);
     712             :     }
     713             : }
     714             : 
     715             : void
     716          75 : vnet_flow_reports_reset (ipfix_exporter_t *exp)
     717             : {
     718             :   flow_report_t *fr;
     719             :   u32 i;
     720             : 
     721         141 :   for (i = 0; i < vec_len (exp->streams); i++)
     722          66 :     if (stream_index_valid (exp, i))
     723          34 :       exp->streams[i].sequence_number = 0;
     724             : 
     725         133 :   vec_foreach (fr, exp->reports)
     726             :     {
     727          58 :       fr->update_rewrite = 1;
     728          58 :       fr->last_template_sent = 0;
     729             :     }
     730          75 : }
     731             : 
     732             : void
     733           0 : vnet_stream_reset (ipfix_exporter_t *exp, u32 stream_index)
     734             : {
     735             :   flow_report_t *fr;
     736             : 
     737           0 :   exp->streams[stream_index].sequence_number = 0;
     738             : 
     739           0 :   vec_foreach (fr, exp->reports)
     740           0 :     if (exp->reports->stream_index == stream_index)
     741             :       {
     742           0 :         fr->update_rewrite = 1;
     743           0 :         fr->last_template_sent = 0;
     744             :       }
     745           0 : }
     746             : 
     747             : int
     748           0 : vnet_stream_change (ipfix_exporter_t *exp, u32 old_domain_id, u16 old_src_port,
     749             :                     u32 new_domain_id, u16 new_src_port)
     750             : {
     751           0 :   i32 stream_index = find_stream (exp, old_domain_id, old_src_port);
     752             : 
     753           0 :   if (stream_index < 0)
     754           0 :     return 1;
     755           0 :   flow_report_stream_t *stream = &exp->streams[stream_index];
     756           0 :   stream->domain_id = new_domain_id;
     757           0 :   stream->src_port = new_src_port;
     758           0 :   if (old_domain_id != new_domain_id || old_src_port != new_src_port)
     759           0 :     vnet_stream_reset (exp, stream_index);
     760           0 :   return 0;
     761             : }
     762             : 
     763             : static clib_error_t *
     764          36 : set_ipfix_exporter_command_fn (vlib_main_t * vm,
     765             :                                unformat_input_t * input,
     766             :                                vlib_cli_command_t * cmd)
     767             : {
     768          36 :   flow_report_main_t *frm = &flow_report_main;
     769          36 :   ip_address_t collector = IP_ADDRESS_V4_ALL_0S, src = IP_ADDRESS_V4_ALL_0S;
     770          36 :   u16 collector_port = UDP_DST_PORT_ipfix;
     771             :   u32 fib_id;
     772          36 :   u32 fib_index = ~0;
     773             : 
     774          36 :   u32 path_mtu = 512;           // RFC 7011 section 10.3.3.
     775          36 :   u32 template_interval = 20;
     776          36 :   u8 udp_checksum = 0;
     777          36 :   ipfix_exporter_t *exp = pool_elt_at_index (frm->exporters, 0);
     778             :   u32 ip_header_size;
     779             : 
     780          72 :   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
     781             :     {
     782          36 :       if (unformat (input, "collector %U", unformat_ip4_address,
     783             :                     &collector.ip.ip4))
     784             :         ;
     785           0 :       else if (unformat (input, "port %U", unformat_udp_port,
     786             :                          &collector_port))
     787             :         ;
     788           0 :       else if (unformat (input, "src %U", unformat_ip4_address, &src.ip.ip4))
     789             :         ;
     790           0 :       else if (unformat (input, "fib-id %u", &fib_id))
     791             :         {
     792           0 :           ip4_main_t *im = &ip4_main;
     793           0 :           uword *p = hash_get (im->fib_index_by_table_id, fib_id);
     794           0 :           if (!p)
     795           0 :             return clib_error_return (0, "fib ID %d doesn't exist\n", fib_id);
     796           0 :           fib_index = p[0];
     797             :         }
     798           0 :       else if (unformat (input, "path-mtu %u", &path_mtu))
     799             :         ;
     800           0 :       else if (unformat (input, "template-interval %u", &template_interval))
     801             :         ;
     802           0 :       else if (unformat (input, "udp-checksum"))
     803           0 :         udp_checksum = 1;
     804             :       else
     805           0 :         break;
     806             :     }
     807             : 
     808             :   /*
     809             :    * If the collector address is set then the src must be too.
     810             :    * Collector address can be set to 0 to disable exporter
     811             :    */
     812          36 :   if (!ip_address_is_zero (&collector) && ip_address_is_zero (&src))
     813           0 :     return clib_error_return (0, "src address required");
     814          36 :   if (collector.version != src.version)
     815           0 :     return clib_error_return (
     816             :       0, "src address and dest address must use same IP version");
     817             : 
     818          36 :   if (path_mtu > 1450 /* vpp does not support fragmentation */ )
     819           0 :     return clib_error_return (0, "too big path-mtu value, maximum is 1450");
     820             : 
     821          36 :   if (path_mtu < 68)
     822           0 :     return clib_error_return (0, "too small path-mtu value, minimum is 68");
     823             : 
     824             :   /* Calculate how much header data we need. */
     825          36 :   if (collector.version == AF_IP4)
     826          36 :     ip_header_size = sizeof (ip4_header_t);
     827             :   else
     828           0 :     ip_header_size = sizeof (ip6_header_t);
     829          36 :   exp->all_headers_size = ip_header_size + sizeof (udp_header_t) +
     830          36 :                           sizeof (ipfix_message_header_t) +
     831             :                           sizeof (ipfix_set_header_t);
     832             : 
     833             :   /* Reset report streams if we are reconfiguring IP addresses */
     834          38 :   if (ip_address_cmp (&exp->ipfix_collector, &collector) ||
     835           2 :       ip_address_cmp (&exp->src_address, &src) ||
     836           2 :       exp->collector_port != collector_port)
     837          34 :     vnet_flow_reports_reset (exp);
     838             : 
     839          36 :   exp->ipfix_collector = collector;
     840          36 :   exp->collector_port = collector_port;
     841          36 :   exp->src_address = src;
     842          36 :   exp->fib_index = fib_index;
     843          36 :   exp->path_mtu = path_mtu;
     844          36 :   exp->template_interval = template_interval;
     845          36 :   exp->udp_checksum = udp_checksum;
     846             : 
     847          36 :   if (collector.ip.ip4.as_u32)
     848           0 :     vlib_cli_output (vm,
     849             :                      "Collector %U, src address %U, "
     850             :                      "fib index %d, path MTU %u, "
     851             :                      "template resend interval %us, "
     852             :                      "udp checksum %s",
     853             :                      format_ip4_address, &exp->ipfix_collector.ip.ip4,
     854             :                      format_ip4_address, &exp->src_address.ip.ip4, fib_index,
     855             :                      path_mtu, template_interval,
     856             :                      udp_checksum ? "enabled" : "disabled");
     857             :   else
     858          36 :     vlib_cli_output (vm, "IPFIX Collector is disabled");
     859             : 
     860             :   /* Turn on the flow reporting process */
     861          36 :   vlib_process_signal_event (vm, flow_report_process_node.index, 1, 0);
     862          36 :   return 0;
     863             : }
     864             : 
     865             : /* *INDENT-OFF* */
     866      272887 : VLIB_CLI_COMMAND (set_ipfix_exporter_command, static) = {
     867             :     .path = "set ipfix exporter",
     868             :     .short_help = "set ipfix exporter "
     869             :                   "collector <ip4-address> [port <port>] "
     870             :                   "src <ip4-address> [fib-id <fib-id>] "
     871             :                   "[path-mtu <path-mtu>] "
     872             :                   "[template-interval <template-interval>] "
     873             :                   "[udp-checksum]",
     874             :     .function = set_ipfix_exporter_command_fn,
     875             : };
     876             : /* *INDENT-ON* */
     877             : 
     878             : 
     879             : static clib_error_t *
     880          71 : ipfix_flush_command_fn (vlib_main_t * vm,
     881             :                         unformat_input_t * input, vlib_cli_command_t * cmd)
     882             : {
     883             :   /* poke the flow reporting process */
     884          71 :   vlib_process_signal_event (vm, flow_report_process_node.index, 1, 0);
     885          71 :   return 0;
     886             : }
     887             : 
     888             : /* *INDENT-OFF* */
     889      272887 : VLIB_CLI_COMMAND (ipfix_flush_command, static) = {
     890             :     .path = "ipfix flush",
     891             :     .short_help = "flush the current ipfix data [for make test]",
     892             :     .function = ipfix_flush_command_fn,
     893             : };
     894             : /* *INDENT-ON* */
     895             : 
     896             : static clib_error_t *
     897         559 : flow_report_init (vlib_main_t * vm)
     898             : {
     899         559 :   flow_report_main_t *frm = &flow_report_main;
     900             :   ipfix_exporter_t *exp;
     901             : 
     902         559 :   frm->vlib_main = vm;
     903         559 :   frm->vnet_main = vnet_get_main ();
     904         559 :   frm->unix_time_0 = time (0);
     905         559 :   frm->vlib_time_0 = vlib_time_now (frm->vlib_main);
     906             :   /*
     907             :    * Make sure that we can always access the first exporter for
     908             :    * backwards compatibility reasons.
     909             :    */
     910         559 :   pool_alloc (frm->exporters, IPFIX_EXPORTERS_MAX);
     911         559 :   pool_get (frm->exporters, exp);
     912             :   /* Verify that this is at index 0 */
     913         559 :   ASSERT (frm->exporters == exp);
     914         559 :   exp->fib_index = ~0;
     915         559 :   return 0;
     916             : }
     917             : 
     918       69439 : VLIB_INIT_FUNCTION (flow_report_init);
     919             : /*
     920             :  * fd.io coding-style-patch-verification: ON
     921             :  *
     922             :  * Local Variables:
     923             :  * eval: (c-set-style "gnu")
     924             :  * End:
     925             :  */

Generated by: LCOV version 1.14