Add rx interface, ip4 src+dst to the flow-per-packet records
[vpp.git] / plugins / flowperpkt-plugin / flowperpkt / node.c
1 /*
2  * node.c - skeleton vpp engine plug-in dual-loop node skeleton
3  *
4  * Copyright (c) <current-year> <your-organization>
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 #include <vlib/vlib.h>
18 #include <vnet/vnet.h>
19 #include <vnet/pg/pg.h>
20 #include <vppinfra/error.h>
21 #include <flowperpkt/flowperpkt.h>
22
23 /**
24  * @file flow record generator graph node
25  */
26
27 typedef struct
28 {
29   /** interface handle */
30   u32 rx_sw_if_index;
31   u32 tx_sw_if_index;
32   u32 src_address;
33   u32 dst_address;
34   /** ToS bits */
35   u8 tos;
36   /** packet timestamp */
37   u64 timestamp;
38   /** size of the buffer */
39   u16 buffer_size;
40 } flowperpkt_trace_t;
41
42 /* packet trace format function */
43 static u8 *
44 format_flowperpkt_trace (u8 * s, va_list * args)
45 {
46   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
47   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
48   flowperpkt_trace_t *t = va_arg (*args, flowperpkt_trace_t *);
49
50   s = format (s,
51               "FLOWPERPKT: rx_sw_if_index %d, tx_sw_if_index %d, src %U dst %U tos %0x2, timestamp %lld, size %d",
52               t->rx_sw_if_index, t->tx_sw_if_index,
53               format_ip4_address, &t->src_address,
54               format_ip4_address, &t->dst_address,
55               t->tos, t->timestamp, t->buffer_size);
56   return s;
57 }
58
59 vlib_node_registration_t flowperpkt_node;
60
61 #define foreach_flowperpkt_error \
62 _(SWAPPED, "Mac swap packets processed")
63
64 typedef enum
65 {
66 #define _(sym,str) FLOWPERPKT_ERROR_##sym,
67   foreach_flowperpkt_error
68 #undef _
69     FLOWPERPKT_N_ERROR,
70 } flowperpkt_error_t;
71
72 static char *flowperpkt_error_strings[] = {
73 #define _(sym,string) string,
74   foreach_flowperpkt_error
75 #undef _
76 };
77
78 typedef enum
79 {
80   FLOWPERPKT_NEXT_DROP,
81   FLOWPERPKT_N_NEXT,
82 } flowperpkt_next_t;
83
84 /**
85  * @brief add an entry to the flow record under construction
86  * @param vm vlib_main_t * current worker thread main structure pointer
87  * @param fm flowperpkt_main_t * flow-per-packet main structure pointer
88  * @param sw_if_index u32 interface handle
89  * @param tos u8 ToS bits from the packet
90  * @param timestamp u64 timestamp, nanoseconds since 1/1/70
91  * @param length u16 ip length of the packet
92  * @param do_flush int 1 = flush all cached records, 0 = construct a record
93  */
94
95 static inline void
96 add_to_flow_record (vlib_main_t * vm,
97                     flowperpkt_main_t * fm,
98                     u32 rx_sw_if_index, u32 tx_sw_if_index,
99                     u32 src_address, u32 dst_address,
100                     u8 tos, u64 timestamp, u16 length, int do_flush)
101 {
102   u32 my_cpu_number = vm->cpu_index;
103   flow_report_main_t *frm = &flow_report_main;
104   ip4_header_t *ip;
105   udp_header_t *udp;
106   ip4_ipfix_template_packet_t *tp;
107   ipfix_message_header_t *h;
108   ipfix_set_header_t *s;
109   vlib_frame_t *f;
110   vlib_buffer_t *b0;
111   u16 offset;
112   u32 bi0;
113   vlib_buffer_free_list_t *fl;
114
115   /* Find or allocate a buffer */
116   b0 = fm->buffers_per_worker[my_cpu_number];
117
118   /* Need to allocate a buffer? */
119   if (PREDICT_FALSE (b0 == 0))
120     {
121       /* Nothing to flush */
122       if (do_flush)
123         return;
124
125       /* $$$$ drop counter? */
126       if (vlib_buffer_alloc (vm, &bi0, 1) != 1)
127         return;
128
129       /* Initialize the buffer */
130       b0 = fm->buffers_per_worker[my_cpu_number] = vlib_get_buffer (vm, bi0);
131       fl =
132         vlib_buffer_get_free_list (vm, VLIB_BUFFER_DEFAULT_FREE_LIST_INDEX);
133       vlib_buffer_init_for_free_list (b0, fl);
134       VLIB_BUFFER_TRACE_TRAJECTORY_INIT (b0);
135       offset = 0;
136     }
137   else
138     {
139       /* use the current buffer */
140       bi0 = vlib_get_buffer_index (vm, b0);
141       offset = fm->next_record_offset_per_worker[my_cpu_number];
142     }
143
144   /* Find or allocate a frame */
145   f = fm->frames_per_worker[my_cpu_number];
146   if (PREDICT_FALSE (f == 0))
147     {
148       u32 *to_next;
149       f = vlib_get_frame_to_node (vm, ip4_lookup_node.index);
150       fm->frames_per_worker[my_cpu_number] = f;
151
152       /* Enqueue the buffer */
153       to_next = vlib_frame_vector_args (f);
154       to_next[0] = bi0;
155       f->n_vectors = 1;
156     }
157
158   /* Fresh packet, construct header */
159   if (PREDICT_FALSE (offset == 0))
160     {
161       flow_report_stream_t *stream;
162
163       stream = &frm->streams[0];
164
165       b0->current_data = 0;
166       b0->current_length = sizeof (*ip) + sizeof (*udp) + sizeof (*h) +
167         sizeof (*s);
168       b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VLIB_BUFFER_FLOW_REPORT);
169       vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0;
170       vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index;
171
172       tp = vlib_buffer_get_current (b0);
173       ip = (ip4_header_t *) & tp->ip4;
174       udp = (udp_header_t *) (ip + 1);
175       h = (ipfix_message_header_t *) (udp + 1);
176       s = (ipfix_set_header_t *) (h + 1);
177
178       ip->ip_version_and_header_length = 0x45;
179       ip->ttl = 254;
180       ip->protocol = IP_PROTOCOL_UDP;
181       ip->flags_and_fragment_offset = 0;
182       ip->src_address.as_u32 = frm->src_address.as_u32;
183       ip->dst_address.as_u32 = frm->ipfix_collector.as_u32;
184       udp->src_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix);
185       udp->dst_port = clib_host_to_net_u16 (UDP_DST_PORT_ipfix);
186
187       /* FIXUP: message header export_time */
188       h->export_time = (u32)
189         (((f64) frm->unix_time_0) +
190          (vlib_time_now (frm->vlib_main) - frm->vlib_time_0));
191       h->export_time = clib_host_to_net_u32 (h->export_time);
192       h->domain_id = clib_host_to_net_u32 (stream->domain_id);
193
194       /* FIXUP: message header sequence_number */
195       h->sequence_number = stream->sequence_number++;
196       h->sequence_number = clib_host_to_net_u32 (h->sequence_number);
197
198       offset = (u32) (((u8 *) (s + 1)) - (u8 *) tp);
199     }
200
201   /* Add data, unless we're flushing stale data */
202   if (PREDICT_TRUE (do_flush == 0))
203     {
204
205       /* Add data */
206       /* Ingress interface */
207       {
208         u32 ingress_interface = clib_host_to_net_u32 (rx_sw_if_index);
209         clib_memcpy (b0->data + offset, &ingress_interface,
210                      sizeof (ingress_interface));
211         offset += sizeof (ingress_interface);
212       }
213       /* Egress interface */
214       {
215         u32 egress_interface = clib_host_to_net_u32 (tx_sw_if_index);
216         clib_memcpy (b0->data + offset, &egress_interface,
217                      sizeof (egress_interface));
218         offset += sizeof (egress_interface);
219       }
220       /* ip4 src address */
221       {
222         clib_memcpy (b0->data + offset, &src_address, sizeof (src_address));
223         offset += sizeof (src_address);
224       }
225       /* ip4 dst address */
226       {
227         clib_memcpy (b0->data + offset, &dst_address, sizeof (dst_address));
228         offset += sizeof (dst_address);
229       }
230
231       /* ToS */
232       b0->data[offset++] = tos;
233
234       /* Timestamp */
235       clib_memcpy (b0->data + offset, &timestamp, sizeof (f64));
236       offset += sizeof (f64);
237
238       /* pkt size */
239       {
240         u16 pkt_size = clib_host_to_net_u16 (length);
241         clib_memcpy (b0->data + offset, &pkt_size, sizeof (pkt_size));
242         offset += sizeof (pkt_size);
243       }
244
245       b0->current_length +=
246         /* sw_if_index + tos + timestamp + length = 15 */
247         4 * sizeof (u32) + sizeof (u8) + sizeof (f64) + sizeof (u16);
248
249     }
250   /* Time to flush the buffer? */
251   if (PREDICT_FALSE
252       (do_flush || (offset + sizeof (u32) + sizeof (u8)
253                     + sizeof (f64)) > frm->path_mtu))
254     {
255       tp = vlib_buffer_get_current (b0);
256       ip = (ip4_header_t *) & tp->ip4;
257       udp = (udp_header_t *) (ip + 1);
258       h = (ipfix_message_header_t *) (udp + 1);
259       s = (ipfix_set_header_t *) (h + 1);
260
261       s->set_id_length = ipfix_set_id_length (256,
262                                               b0->current_length -
263                                               (sizeof (*ip) + sizeof (*udp) +
264                                                sizeof (*h)));
265       h->version_length = version_length (b0->current_length -
266                                           (sizeof (*ip) + sizeof (*udp)));
267
268       ip->length = clib_host_to_net_u16 (b0->current_length);
269
270       ip->checksum = ip4_header_checksum (ip);
271       udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip));
272
273       if (frm->udp_checksum)
274         {
275           /* RFC 7011 section 10.3.2. */
276           udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip);
277           if (udp->checksum == 0)
278             udp->checksum = 0xffff;
279         }
280
281       ASSERT (ip->checksum == ip4_header_checksum (ip));
282
283       vlib_put_frame_to_node (vm, ip4_lookup_node.index,
284                               fm->frames_per_worker[my_cpu_number]);
285       fm->frames_per_worker[my_cpu_number] = 0;
286       fm->buffers_per_worker[my_cpu_number] = 0;
287       offset = 0;
288     }
289
290   fm->next_record_offset_per_worker[my_cpu_number] = offset;
291 }
292
293 void
294 flowperpkt_flush_callback (void)
295 {
296   vlib_main_t *vm = vlib_get_main ();
297   flowperpkt_main_t *fm = &flowperpkt_main;
298
299   add_to_flow_record (vm, fm, 0 /* rx_sw_if_index */ ,
300                       0 /* tx_sw_if_index */ ,
301                       0 /* src_address */ ,
302                       0 /* dst_address */ ,
303                       0 /* ToS */ ,
304                       0ULL /* timestamp */ ,
305                       0 /* length */ ,
306                       1 /* do_flush */ );
307 }
308
309
310 static uword
311 flowperpkt_node_fn (vlib_main_t * vm,
312                     vlib_node_runtime_t * node, vlib_frame_t * frame)
313 {
314   u32 n_left_from, *from, *to_next;
315   flowperpkt_next_t next_index;
316   flowperpkt_main_t *fm = &flowperpkt_main;
317   u64 now;
318
319   now = (u64) ((vlib_time_now (vm) - fm->vlib_time_0) * 1e9);
320   now += fm->nanosecond_time_0;
321
322   from = vlib_frame_vector_args (frame);
323   n_left_from = frame->n_vectors;
324   next_index = node->cached_next_index;
325
326   while (n_left_from > 0)
327     {
328       u32 n_left_to_next;
329
330       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
331
332       while (n_left_from >= 4 && n_left_to_next >= 2)
333         {
334           u32 next0 = FLOWPERPKT_NEXT_DROP;
335           u32 next1 = FLOWPERPKT_NEXT_DROP;
336           ip4_header_t *ip0, *ip1;
337           u16 len0, len1;
338           u32 bi0, bi1;
339           vlib_buffer_t *b0, *b1;
340
341           /* Prefetch next iteration. */
342           {
343             vlib_buffer_t *p2, *p3;
344
345             p2 = vlib_get_buffer (vm, from[2]);
346             p3 = vlib_get_buffer (vm, from[3]);
347
348             vlib_prefetch_buffer_header (p2, LOAD);
349             vlib_prefetch_buffer_header (p3, LOAD);
350
351             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
352             CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
353           }
354
355           /* speculatively enqueue b0 and b1 to the current next frame */
356           to_next[0] = bi0 = from[0];
357           to_next[1] = bi1 = from[1];
358           from += 2;
359           to_next += 2;
360           n_left_from -= 2;
361           n_left_to_next -= 2;
362
363           b0 = vlib_get_buffer (vm, bi0);
364           b1 = vlib_get_buffer (vm, bi1);
365
366           vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_TX],
367                              &next0, b0);
368           vnet_feature_next (vnet_buffer (b1)->sw_if_index[VLIB_TX],
369                              &next1, b1);
370
371           ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) +
372                                   vnet_buffer (b0)->ip.save_rewrite_length);
373
374           len0 = vlib_buffer_length_in_chain (vm, b0);
375
376           if (PREDICT_TRUE ((b0->flags & VLIB_BUFFER_FLOW_REPORT) == 0))
377             add_to_flow_record (vm, fm,
378                                 vnet_buffer (b0)->sw_if_index[VLIB_RX],
379                                 vnet_buffer (b0)->sw_if_index[VLIB_TX],
380                                 ip0->src_address.as_u32,
381                                 ip0->dst_address.as_u32,
382                                 ip0->tos, now, len0, 0 /* flush */ );
383
384           ip1 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b1) +
385                                   vnet_buffer (b1)->ip.save_rewrite_length);
386           len1 = vlib_buffer_length_in_chain (vm, b1);
387
388           if (PREDICT_TRUE ((b1->flags & VLIB_BUFFER_FLOW_REPORT) == 0))
389             add_to_flow_record (vm, fm,
390                                 vnet_buffer (b1)->sw_if_index[VLIB_RX],
391                                 vnet_buffer (b1)->sw_if_index[VLIB_TX],
392                                 ip1->src_address.as_u32,
393                                 ip1->dst_address.as_u32,
394                                 ip1->tos, now, len1, 0 /* flush */ );
395
396           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
397             {
398               if (b0->flags & VLIB_BUFFER_IS_TRACED)
399                 {
400                   flowperpkt_trace_t *t =
401                     vlib_add_trace (vm, node, b0, sizeof (*t));
402                   t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
403                   t->tx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
404                   t->src_address = ip0->src_address.as_u32;
405                   t->dst_address = ip0->dst_address.as_u32;
406                   t->tos = ip0->tos;
407                   t->timestamp = now;
408                   t->buffer_size = len0;
409                 }
410               if (b1->flags & VLIB_BUFFER_IS_TRACED)
411                 {
412                   flowperpkt_trace_t *t =
413                     vlib_add_trace (vm, node, b1, sizeof (*t));
414                   t->rx_sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_RX];
415                   t->tx_sw_if_index = vnet_buffer (b1)->sw_if_index[VLIB_TX];
416                   t->src_address = ip1->src_address.as_u32;
417                   t->dst_address = ip1->dst_address.as_u32;
418                   t->tos = ip1->tos;
419                   t->timestamp = now;
420                   t->buffer_size = len1;
421                 }
422             }
423
424           /* verify speculative enqueues, maybe switch current next frame */
425           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
426                                            to_next, n_left_to_next,
427                                            bi0, bi1, next0, next1);
428         }
429
430       while (n_left_from > 0 && n_left_to_next > 0)
431         {
432           u32 bi0;
433           vlib_buffer_t *b0;
434           u32 next0 = FLOWPERPKT_NEXT_DROP;
435           ip4_header_t *ip0;
436           u16 len0;
437
438           /* speculatively enqueue b0 to the current next frame */
439           bi0 = from[0];
440           to_next[0] = bi0;
441           from += 1;
442           to_next += 1;
443           n_left_from -= 1;
444           n_left_to_next -= 1;
445
446           b0 = vlib_get_buffer (vm, bi0);
447
448           vnet_feature_next (vnet_buffer (b0)->sw_if_index[VLIB_TX],
449                              &next0, b0);
450
451           ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) +
452                                   vnet_buffer (b0)->ip.save_rewrite_length);
453           /*
454            * egressInterface, TLV type 14, u32
455            * ipClassOfService, TLV type 5, u8
456            * flowStartNanoseconds, TLV type 156, dateTimeNanoseconds (f64)
457            *   Implementation: f64 nanoseconds since VPP started
458            * dataLinkFrameSize, TLV type 312, u16
459            */
460           len0 = vlib_buffer_length_in_chain (vm, b0);
461
462           if (PREDICT_TRUE ((b0->flags & VLIB_BUFFER_FLOW_REPORT) == 0))
463             add_to_flow_record (vm, fm,
464                                 vnet_buffer (b0)->sw_if_index[VLIB_RX],
465                                 vnet_buffer (b0)->sw_if_index[VLIB_TX],
466                                 ip0->src_address.as_u32,
467                                 ip0->dst_address.as_u32,
468                                 ip0->tos, now, len0, 0 /* flush */ );
469
470           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
471                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
472             {
473               flowperpkt_trace_t *t =
474                 vlib_add_trace (vm, node, b0, sizeof (*t));
475               t->rx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
476               t->tx_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
477               t->src_address = ip0->src_address.as_u32;
478               t->dst_address = ip0->dst_address.as_u32;
479               t->tos = ip0->tos;
480               t->timestamp = now;
481               t->buffer_size = len0;
482             }
483
484           /* verify speculative enqueue, maybe switch current next frame */
485           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
486                                            to_next, n_left_to_next,
487                                            bi0, next0);
488         }
489
490       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
491     }
492   return frame->n_vectors;
493 }
494
495 /**
496  * @brief IPFIX flow-per-packet graph node
497  * @node flowperpkt
498  *
499  * This is the IPFIX flow-record-per-packet node.
500  *
501  * @param vm    vlib_main_t corresponding to the current thread.
502  * @param node  vlib_node_runtime_t data for this node.
503  * @param frame vlib_frame_t whose contents should be dispatched.
504  *
505  * @par Graph mechanics: buffer metadata, next index usage
506  *
507  * <em>Uses:</em>
508  * - <code>vnet_buffer(b)->ip.save_rewrite_length</code>
509  *     - tells the node the length of the rewrite which was applied in
510  *       ip4/6_rewrite_inline, allows the code to find the IP header without
511  *       having to parse L2 headers, or make stupid assumptions about their
512  *       length.
513  * - <code>vnet_buffer(b)->flags & VLIB_BUFFER_FLOW_REPORT</code>
514  *     - Used to suppress flow record generation for flow record packets.
515  *
516  * <em>Sets:</em>
517  * - <code>vnet_buffer(b)->flags & VLIB_BUFFER_FLOW_REPORT</code>
518  *     - To suppress flow record generation for flow record packets
519  *
520  * <em>Next Index:</em>
521  * - Next configured output feature on the interface, usually
522  *   "interface-output." Generated flow records head for ip4-lookup
523  */
524
525 /* *INDENT-OFF* */
526 VLIB_REGISTER_NODE (flowperpkt_node) = {
527   .function = flowperpkt_node_fn,
528   .name = "flowperpkt",
529   .vector_size = sizeof (u32),
530   .format_trace = format_flowperpkt_trace,
531   .type = VLIB_NODE_TYPE_INTERNAL,
532
533   .n_errors = ARRAY_LEN(flowperpkt_error_strings),
534   .error_strings = flowperpkt_error_strings,
535
536   .n_next_nodes = FLOWPERPKT_N_NEXT,
537
538   /* edit / add dispositions here */
539   .next_nodes = {
540     [FLOWPERPKT_NEXT_DROP] = "error-drop",
541   },
542 };
543 /* *INDENT-ON* */
544
545 /*
546  * fd.io coding-style-patch-verification: ON
547  *
548  * Local Variables:
549  * eval: (c-set-style "gnu")
550  * End:
551  */