summaryrefslogtreecommitdiffstats
path: root/src/vnet
diff options
context:
space:
mode:
authorBenoît Ganne <bganne@cisco.com>2023-01-05 10:56:26 +0100
committerDamjan Marion <dmarion@0xa5.net>2023-03-06 14:26:26 +0000
commit1ee334c6f86b3e814aca1443ec7dfccdfc34ab26 (patch)
treeefa113d19b2b789dfed054546ded13a12f74d553 /src/vnet
parentd69233f44ea1f039e7755358c0acb20bfbec2258 (diff)
crypto: make it easier to diagnose keys use-after-free
Type: improvement Change-Id: Ib98eba146e24e659acf3b9a228b81fcd641f4c67 Signed-off-by: Benoît Ganne <bganne@cisco.com>
Diffstat (limited to 'src/vnet')
-rw-r--r--src/vnet/crypto/crypto.c4
1 files changed, 2 insertions, 2 deletions
diff --git a/src/vnet/crypto/crypto.c b/src/vnet/crypto/crypto.c
index f8926c9734a..156dab46517 100644
--- a/src/vnet/crypto/crypto.c
+++ b/src/vnet/crypto/crypto.c
@@ -469,12 +469,12 @@ vnet_crypto_key_del (vlib_main_t * vm, vnet_crypto_key_index_t index)
if (key->type == VNET_CRYPTO_KEY_TYPE_DATA)
{
- clib_memset (key->data, 0, vec_len (key->data));
+ clib_memset (key->data, 0xfe, vec_len (key->data));
vec_free (key->data);
}
else if (key->type == VNET_CRYPTO_KEY_TYPE_LINK)
{
- key->index_crypto = key->index_integ = 0;
+ key->index_crypto = key->index_integ = ~0;
}
pool_put (cm->keys, key);
='n195' href='#n195'>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
# IPFIX support {#ipfix}

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.

```{.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":


```{.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

```{.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

   ```{.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 (ip->checksum == ip4_header_checksum (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.

   ```{.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);
         VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
         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);
   }  

```