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