summaryrefslogtreecommitdiffstats
path: root/docs/_static
AgeCommit message (Expand)AuthorFilesLines
2018-08-10DOC-ONLY: build system detailsDave Barach1-0/+14
2018-07-26Initial commit of Sphinx docsJohn DeNisco3-0/+5
> 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
.. _ipfix_doc:

IPFIX support
=============

VPP includes a high-performance IPFIX record exporter. This note
explains how to use the internal APIs to export IPFIX data, and how to
configure and send the required IPFIX templates.

As you’ll see, a bit of typing is required.

First: create an ipfix “report”
-------------------------------

Include the flow report header file, fill out a @ref
vnet_flow_report_add_del_args_t structure, and call
vnet_flow_report_add_del.

.. code:: c

      #include <vnet/ipfix-export/flow_report.h>
      /* Defined in flow_report.h, of interest when constructing reports */

      /* ipfix field definitions for a particular report */
      typedef struct
      {
        u32 info_element;
        u32 size;
      } ipfix_report_element_t;

      /* Report add/del argument structure */
      typedef struct
      {
        /* Callback to flush current ipfix packet / frame */
        vnet_flow_data_callback_t *flow_data_callback;

        /* Callback to build the template packet rewrite string */
        vnet_flow_rewrite_callback_t *rewrite_callback;

        /* List of ipfix elements in the report */
        ipfix_report_element_t *report_elements;
        u32 n_report_elements;
        /* Kept in flow report, used e.g. by flow classifier */
        opaque_t opaque;
        /* Add / delete a report */
        int is_add;
        /* Ipfix "domain-ID", see RFC, set as desired */
        u32 domain_id;
        /* ipfix packet source port, often set to UDP_DST_PORT_ipfix */
        u16 src_port;
        /* Set by ipfix infra, needed to send data packets */
        u32 *stream_indexp;
      } vnet_flow_report_add_del_args_t;

      /* Private header file contents */

      /* Report ipfix element definition */
      #define foreach_simple_report_ipfix_element     \
      _(sourceIPv4Address, 4)                         \
      _(destinationIPv4Address, 4)                    \
      _(sourceTransportPort, 2)                       \
      _(destinationTransportPort, 2)                  \
      _(protocolIdentifier, 1)                        \
      _(flowStartMicroseconds, 8)                     \
      _(flowEndMicroseconds, 8)

      static ipfix_report_element_t simple_report_elements[] = {
      #define _(a,b) {a,b},
        foreach_simple_report_ipfix_element
      #undef _
      };

      typedef struct
      {
        /** Buffers and frames, per thread */
        vlib_buffer_t **buffers_by_thread;
        vlib_frame_t **frames_by_thread;
        u32 *next_record_offset_by_thread;

        /** Template ID's */
        u16 *template_ids;

        /** Time reference pair */
        u64 usec_time_0;
        f64 vlib_time_0;

        /** Stream index */
        u32 stream_index;

        /* Convenience */
        flow_report_main_t *flow_report_main;
        vlib_main_t *vlib_main;
        vnet_main_t *vnet_main;
      } my_logging_main_t;

      extern my_logging_main_t my_logging_main;

      ...

      /* Recitations */
      flow_report_main_t *frm = &flow_report_main;
      my_logging_main_t *mlm = &my_logging_main;
      vnet_flow_report_add_del_args_t a;
      int rv;
      u16 template_id;

      ...

      /* Init function: set up time reference pair */
      mlm->vlib_time_0 = vlib_time_now (vm);
      mlm->milisecond_time_0 = unix_time_now_nsec () * 1e-6;

      ...

      /* Create a report */
      memset (&a, 0, sizeof (a));
      a.is_add = 1 /* to enable the report */;
      a.domain_id = 1 /* pick a domain ID */;
      a.src_port = UDP_DST_PORT_ipfix /* src port for reports */;

      /* Use the generic template packet rewrite string generator */
      a.rewrite_callback = vnet_flow_rewrite_generic_callback;

      /* Supply a list of ipfix report elements */
      a.report_elements = simple_report_elements;
      a.n_report_elements = ARRAY_LEN (simple_report_elements);

      /* Pointer to the ipfix stream index, set by the report infra */
      a.stream_indexp = &mlm->stream_index;
      a.flow_data_callback = my_flow_data_callback;

      /* Create the report */
      rv = vnet_flow_report_add_del (frm, &a, &template_id);
      if (rv)
        oops...

      /* Save the template-ID for later use */
      mlm->template_id = template_id;

Several things are worth describing in more detail.

vnet_flow_rewrite_generic_callback programming
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This generic callback helps build ipfix template packets. When
registering an ipfix report, pass an (array, count) of ipfix elements as
shown above.

my_flow_data_callback
~~~~~~~~~~~~~~~~~~~~~

The ipfix flow export infrastructure calls this callback to flush the
current ipfix packet; to make sure that ipfix data is not retained for
an unreasonably long period of time.

We typically code it as shown below, to call an application-specific
function with (uninteresting arguments), and “do_flush = 1”:

.. code:: c


         vlib_frame_t *my_flow_data_callback
                      (flow_report_main_t * frm,
                  flow_report_t * fr,
              vlib_frame_t * f,
              u32 * to_next, u32 node_index)
         {

            my_buffer_flow_record (0, ... , 0, 1 /* do_flush */);
            return f;
         }

my_flow_data_header
~~~~~~~~~~~~~~~~~~~

This function creates the packet header for an ipfix data packet

.. code:: c


      static inline void
      my_flow_report_header (flow_report_main_t * frm,
                 vlib_buffer_t * b0, u32 * offset)
      {
         my_logging_main_t *mlm = &my_logging_main;
         flow_report_stream_t *stream;
         ip4_ipfix_template_packet_t *tp;
         ipfix_message_header_t *h = 0;


         ipfix_set_header_t *s = 0;
         ip4_header_t *ip;
         udp_header_t *udp;

         stream = &frm->streams[mlm->stream_index];

         b0->current_data = 0;
         b0->current_length = sizeof (*ip) + sizeof (*udp) + sizeof (*h) +
           sizeof (*s);
         b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_FLOW_REPORT);
         vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
         vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index;
         tp = vlib_buffer_get_current (b0);
         ip = (ip4_header_t *) & tp->ip4;
         udp = (udp_header_t *) (ip + 1);
         h = (ipfix_message_header_t *) (udp + 1);
         s = (ipfix_set_header_t *) (h + 1);

         ip->ip_version_and_header_length = 0x45;
         ip->ttl = 254;
         ip->protocol = IP_PROTOCOL_UDP;
         ip->flags_and_fragment_offset = 0;
         ip->src_address.as_u32 = frm->src_address.as_u32;
         ip->dst_address.as_u32 = frm->ipfix_collector.as_u32;
         udp->src_port = clib_host_to_net_u16 (stream->src_port);
         udp->dst_port = clib_host_to_net_u16 (frm->collector_port);
         udp->checksum = 0;

         h->export_time = clib_host_to_net_u32 ((u32)
                                (((f64) frm->unix_time_0) +
                                 (vlib_time_now (frm->vlib_main) -
                                  frm->vlib_time_0)));
            h->sequence_number = clib_host_to_net_u32 (stream->sequence_number++);
            h->domain_id = clib_host_to_net_u32 (stream->domain_id);

            *offset = (u32) (((u8 *) (s + 1)) - (u8 *) tp);
      }

### fixup and transmit a flow record

.. code:: c


      static inline void
      my_send_ipfix_pkt (flow_report_main_t * frm,
                 vlib_frame_t * f, vlib_buffer_t * b0, u16 template_id)
      {
        ip4_ipfix_template_packet_t *tp;
        ipfix_message_header_t *h = 0;
        ipfix_set_header_t *s = 0;
        ip4_header_t *ip;
        udp_header_t *udp;
        vlib_main_t *vm = frm->vlib_main;

        tp = vlib_buffer_get_current (b0);
        ip = (ip4_header_t *) & tp->ip4;
        udp = (udp_header_t *) (ip + 1);
        h = (ipfix_message_header_t *) (udp + 1);
        s = (ipfix_set_header_t *) (h + 1);

        s->set_id_length = ipfix_set_id_length (template_id,
                          b0->current_length -
                          (sizeof (*ip) + sizeof (*udp) +
                           sizeof (*h)));
        h->version_length = version_length (b0->current_length -
                          (sizeof (*ip) + sizeof (*udp)));

        ip->length = clib_host_to_net_u16 (b0->current_length);
        ip->checksum = ip4_header_checksum (ip);
        udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));

        if (frm->udp_checksum)
          {
            udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
            if (udp->checksum == 0)
        udp->checksum = 0xffff;
          }

        ASSERT (ip4_header_checksum_is_valid (ip));

        vlib_put_frame_to_node (vm, ip4_lookup_node.index, f);
      }

### my_buffer_flow_record

This is the key routine which paints individual flow records into an
ipfix packet under construction. It’s pretty straightforward (albeit
stateful) vpp data-plane code. The code shown below is thread-safe by
construction.

.. code:: c

   static inline void
   my_buffer_flow_record_internal (my_flow_record_t * rp, int do_flush,
                                       u32 thread_index)
   {
     vlib_main_t *vm = vlib_mains[thread_index];
     my_logging_main_t *mlm = &jvp_ipfix_main;
     flow_report_main_t *frm = &flow_report_main;
     vlib_frame_t *f;
     vlib_buffer_t *b0 = 0;
     u32 bi0 = ~0;
     u32 offset;

     b0 = mlm->buffers_by_thread[thread_index];

     if (PREDICT_FALSE (b0 == 0))
       {
         if (do_flush)
    return;

         if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
    {
      clib_warning ("can't allocate ipfix data buffer");
      return;
    }

         b0 = vlib_get_buffer (vm, bi0);
         offset = 0;
         mlm->buffers_by_thread[thread_index] = b0;
       }
     else
       {
         bi0 = vlib_get_buffer_index (vm, b0);
         offset = mlm->next_record_offset_by_thread[thread_index];
       }

     f = mlm->frames_by_thread[thread_index];
     if (PREDICT_FALSE (f == 0))
       {
         u32 *to_next;
         f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
         mlm->frames_by_thread[thread_index] = f;
         to_next = vlib_frame_vector_args (f);
         to_next[0] = bi0;
         f->n_vectors = 1;
         mlm->frames_by_thread[thread_index] = f;
       }

     if (PREDICT_FALSE (offset == 0))
       my_flow_report_header (frm, b0, &offset);

     if (PREDICT_TRUE (do_flush == 0))
       {
         /* Paint the new ipfix data record into the buffer */
         clib_memcpy (b0->data + offset, rp, sizeof (*rp));
         offset += sizeof (*rp);
         b0->current_length += sizeof (*rp);
       }

     if (PREDICT_FALSE (do_flush || (offset + sizeof (*rp)) > frm->path_mtu))
       {
         /* Nothing to send? */
         if (offset == 0)
    return;

         send_ipfix_pkt (frm, f, b0, mlm->template_ids[0]);
         mlm->buffers_by_thread[thread_index] = 0;
         mlm->frames_by_thread[thread_index] = 0;
         offset = 0;
       }
     mlm->next_record_offset_by_thread[thread_index] = offset;
   }

   static void
   my_buffer_flow_record (my_flow_record_t * rp, int do_flush)
   {
     u32 thread_index = vlib_get_thread_index();
     my_buffer_flow_record_internal (rp, do_flush, thread_index);
   }