API:replaced all REPLY_MACRO's with api_helper_macros.h
[vpp.git] / src / plugins / flowperpkt / flowperpkt.c
1 /*
2  * flowperpkt.c - per-packet data capture flow report plugin
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
18 /**
19  * @file
20  * @brief Per-packet IPFIX flow record generator plugin
21  *
22  * This file implements vpp plugin registration mechanics,
23  * debug CLI, and binary API handling.
24  */
25
26 #include <vnet/vnet.h>
27 #include <vpp/app/version.h>
28 #include <vnet/plugin/plugin.h>
29 #include <flowperpkt/flowperpkt.h>
30
31 #include <vlibapi/api.h>
32 #include <vlibmemory/api.h>
33 #include <vlibsocket/api.h>
34
35 /* define message IDs */
36 #include <flowperpkt/flowperpkt_msg_enum.h>
37
38 /* define message structures */
39 #define vl_typedefs
40 #include <flowperpkt/flowperpkt_all_api_h.h>
41 #undef vl_typedefs
42
43 /* define generated endian-swappers */
44 #define vl_endianfun
45 #include <flowperpkt/flowperpkt_all_api_h.h>
46 #undef vl_endianfun
47
48 /* instantiate all the print functions we know about */
49 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
50 #define vl_printfun
51 #include <flowperpkt/flowperpkt_all_api_h.h>
52 #undef vl_printfun
53
54 flowperpkt_main_t flowperpkt_main;
55
56 /* Get the API version number */
57 #define vl_api_version(n,v) static u32 api_version=(v);
58 #include <flowperpkt/flowperpkt_all_api_h.h>
59 #undef vl_api_version
60
61 #define REPLY_MSG_ID_BASE fm->msg_id_base
62 #include <vlibapi/api_helper_macros.h>
63
64 /* Define the per-interface configurable features */
65 /* *INDENT-OFF* */
66 VNET_FEATURE_INIT (flow_perpacket_ipv4, static) =
67 {
68   .arc_name = "ip4-output",
69   .node_name = "flowperpkt-ipv4",
70   .runs_before = VNET_FEATURES ("interface-output"),
71 };
72
73 VNET_FEATURE_INIT (flow_perpacket_l2, static) =
74 {
75   .arc_name = "interface-output",
76   .node_name = "flowperpkt-l2",
77   .runs_before = VNET_FEATURES ("interface-tx"),
78 };
79 /* *INDENT-ON* */
80
81 /* Macro to finish up custom dump fns */
82 #define FINISH                                  \
83     vec_add1 (s, 0);                            \
84     vl_print (handle, (char *)s);               \
85     vec_free (s);                               \
86     return handle;
87
88 /**
89  * @brief Create an IPFIX template packet rewrite string
90  * @param frm flow_report_main_t *
91  * @param fr flow_report_t *
92  * @param collector_address ip4_address_t * the IPFIX collector address
93  * @param src_address ip4_address_t * the source address we should use
94  * @param collector_port u16 the collector port we should use, host byte order
95  * @returns u8 * vector containing the indicated IPFIX template packet
96  */
97 static inline u8 *
98 flowperpkt_template_rewrite_inline (flow_report_main_t * frm,
99                                     flow_report_t * fr,
100                                     ip4_address_t * collector_address,
101                                     ip4_address_t * src_address,
102                                     u16 collector_port, int variant)
103 {
104   ip4_header_t *ip;
105   udp_header_t *udp;
106   ipfix_message_header_t *h;
107   ipfix_set_header_t *s;
108   ipfix_template_header_t *t;
109   ipfix_field_specifier_t *f;
110   ipfix_field_specifier_t *first_field;
111   u8 *rewrite = 0;
112   ip4_ipfix_template_packet_t *tp;
113   u32 field_count = 0;
114   flow_report_stream_t *stream;
115   flowperpkt_main_t *fm = &flowperpkt_main;
116
117   stream = &frm->streams[fr->stream_index];
118
119   if (variant == FLOW_VARIANT_IPV4)
120     {
121       /*
122        * ip4 Supported Fields:
123        *
124        * ingressInterface, TLV type 10, u32
125        * egressInterface, TLV type 14, u32
126        * sourceIpv4Address, TLV type 8, u32
127        * destinationIPv4Address, TLV type 12, u32
128        * ipClassOfService, TLV type 5, u8
129        * flowStartNanoseconds, TLV type 156, dateTimeNanoseconds (f64)
130        *   Implementation: f64 nanoseconds since VPP started
131        *   warning: wireshark doesn't really understand this TLV
132        * dataLinkFrameSize, TLV type 312, u16
133        *   warning: wireshark doesn't understand this TLV at all
134        */
135
136       /* Currently 7 fields */
137       field_count += 7;
138
139       /* allocate rewrite space */
140       vec_validate_aligned
141         (rewrite,
142          sizeof (ip4_ipfix_template_packet_t)
143          + field_count * sizeof (ipfix_field_specifier_t) - 1,
144          CLIB_CACHE_LINE_BYTES);
145     }
146   else if (variant == FLOW_VARIANT_L2)
147     {
148       /*
149        * L2 Supported Fields:
150        *
151        * ingressInterface, TLV type 10, u32
152        * egressInterface, TLV type 14, u32
153        * sourceMacAddress, TLV type 56, u8[6] we hope
154        * destinationMacAddress, TLV type 57, u8[6] we hope
155        * ethernetType, TLV type 256, u16
156        * flowStartNanoseconds, TLV type 156, dateTimeNanoseconds (f64)
157        *   Implementation: f64 nanoseconds since VPP started
158        *   warning: wireshark doesn't really understand this TLV
159        * dataLinkFrameSize, TLV type 312, u16
160        *   warning: wireshark doesn't understand this TLV at all
161        */
162
163       /* Currently 7 fields */
164       field_count += 7;
165
166       /* allocate rewrite space */
167       vec_validate_aligned
168         (rewrite,
169          sizeof (ip4_ipfix_template_packet_t)
170          + field_count * sizeof (ipfix_field_specifier_t) - 1,
171          CLIB_CACHE_LINE_BYTES);
172     }
173
174   tp = (ip4_ipfix_template_packet_t *) rewrite;
175   ip = (ip4_header_t *) & tp->ip4;
176   udp = (udp_header_t *) (ip + 1);
177   h = (ipfix_message_header_t *) (udp + 1);
178   s = (ipfix_set_header_t *) (h + 1);
179   t = (ipfix_template_header_t *) (s + 1);
180   first_field = f = (ipfix_field_specifier_t *) (t + 1);
181
182   ip->ip_version_and_header_length = 0x45;
183   ip->ttl = 254;
184   ip->protocol = IP_PROTOCOL_UDP;
185   ip->src_address.as_u32 = src_address->as_u32;
186   ip->dst_address.as_u32 = collector_address->as_u32;
187   udp->src_port = clib_host_to_net_u16 (stream->src_port);
188   udp->dst_port = clib_host_to_net_u16 (collector_port);
189   udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip));
190
191   /* FIXUP: message header export_time */
192   /* FIXUP: message header sequence_number */
193   h->domain_id = clib_host_to_net_u32 (stream->domain_id);
194
195   /* Add TLVs to the template */
196   if (variant == FLOW_VARIANT_IPV4)
197     {
198       f->e_id_length =
199         ipfix_e_id_length (0 /* enterprise */ , ingressInterface,
200                            4);
201       f++;
202       f->e_id_length =
203         ipfix_e_id_length (0 /* enterprise */ , egressInterface,
204                            4);
205       f++;
206       f->e_id_length =
207         ipfix_e_id_length (0 /* enterprise */ , sourceIPv4Address,
208                            4);
209       f++;
210       f->e_id_length =
211         ipfix_e_id_length (0 /* enterprise */ , destinationIPv4Address, 4);
212       f++;
213       f->e_id_length =
214         ipfix_e_id_length (0 /* enterprise */ , ipClassOfService,
215                            1);
216       f++;
217       f->e_id_length =
218         ipfix_e_id_length (0 /* enterprise */ , flowStartNanoseconds,
219                            8);
220       f++;
221       f->e_id_length =
222         ipfix_e_id_length (0 /* enterprise */ , dataLinkFrameSize,
223                            2);
224       f++;
225     }
226   else if (variant == FLOW_VARIANT_L2)
227     {
228       f->e_id_length =
229         ipfix_e_id_length (0 /* enterprise */ , ingressInterface,
230                            4);
231       f++;
232       f->e_id_length =
233         ipfix_e_id_length (0 /* enterprise */ , egressInterface,
234                            4);
235       f++;
236       f->e_id_length =
237         ipfix_e_id_length (0 /* enterprise */ , sourceMacAddress,
238                            6);
239       f++;
240       f->e_id_length =
241         ipfix_e_id_length (0 /* enterprise */ , destinationMacAddress, 6);
242       f++;
243       f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , ethernetType,
244                                           2);
245       f++;
246       f->e_id_length =
247         ipfix_e_id_length (0 /* enterprise */ , flowStartNanoseconds,
248                            8);
249       f++;
250       f->e_id_length =
251         ipfix_e_id_length (0 /* enterprise */ , dataLinkFrameSize,
252                            2);
253       f++;
254     }
255
256   /* Extend in the obvious way, right here... */
257
258   /* Back to the template packet... */
259   ip = (ip4_header_t *) & tp->ip4;
260   udp = (udp_header_t *) (ip + 1);
261
262   ASSERT (f - first_field);
263   /* Field count in this template */
264   t->id_count = ipfix_id_count (fr->template_id, f - first_field);
265
266   if (variant == FLOW_VARIANT_IPV4)
267     fm->ipv4_report_id = fr->template_id;
268   else if (variant == FLOW_VARIANT_L2)
269     fm->l2_report_id = fr->template_id;
270
271   /* set length in octets */
272   s->set_id_length =
273     ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
274
275   /* message length in octets */
276   h->version_length = version_length ((u8 *) f - (u8 *) h);
277
278   ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip);
279   ip->checksum = ip4_header_checksum (ip);
280
281   return rewrite;
282 }
283
284 u8 *
285 flowperpkt_template_rewrite_ipv4 (flow_report_main_t * frm,
286                                   flow_report_t * fr,
287                                   ip4_address_t * collector_address,
288                                   ip4_address_t * src_address,
289                                   u16 collector_port)
290 {
291   return flowperpkt_template_rewrite_inline
292     (frm, fr, collector_address, src_address, collector_port,
293      FLOW_VARIANT_IPV4);
294 }
295
296 u8 *
297 flowperpkt_template_rewrite_l2 (flow_report_main_t * frm,
298                                 flow_report_t * fr,
299                                 ip4_address_t * collector_address,
300                                 ip4_address_t * src_address,
301                                 u16 collector_port)
302 {
303   return flowperpkt_template_rewrite_inline
304     (frm, fr, collector_address, src_address, collector_port,
305      FLOW_VARIANT_L2);
306 }
307
308
309 /**
310  * @brief Flush accumulated data
311  * @param frm flow_report_main_t *
312  * @param fr flow_report_t *
313  * @param f vlib_frame_t *
314  *
315  * <em>Notes:</em>
316  * This function must simply return the incoming frame, or no template packets
317  * will be sent.
318  */
319 vlib_frame_t *
320 flowperpkt_data_callback_ipv4 (flow_report_main_t * frm,
321                                flow_report_t * fr,
322                                vlib_frame_t * f, u32 * to_next,
323                                u32 node_index)
324 {
325   flowperpkt_flush_callback_ipv4 ();
326   return f;
327 }
328
329 vlib_frame_t *
330 flowperpkt_data_callback_l2 (flow_report_main_t * frm,
331                              flow_report_t * fr,
332                              vlib_frame_t * f, u32 * to_next, u32 node_index)
333 {
334   flowperpkt_flush_callback_l2 ();
335   return f;
336 }
337
338 /**
339  * @brief configure / deconfigure the IPFIX flow-per-packet
340  * @param fm flowperpkt_main_t * fm
341  * @param sw_if_index u32 the desired interface
342  * @param is_add int 1 to enable the feature, 0 to disable it
343  * @returns 0 if successful, non-zero otherwise
344  */
345
346 static int flowperpkt_tx_interface_add_del_feature
347   (flowperpkt_main_t * fm, u32 sw_if_index, int which, int is_add)
348 {
349   flow_report_main_t *frm = &flow_report_main;
350   vnet_flow_report_add_del_args_t _a, *a = &_a;
351   int rv;
352
353   if (which == FLOW_VARIANT_IPV4 && !fm->ipv4_report_created)
354     {
355       memset (a, 0, sizeof (*a));
356       a->rewrite_callback = flowperpkt_template_rewrite_ipv4;
357       a->flow_data_callback = flowperpkt_data_callback_ipv4;
358       a->is_add = 1;
359       a->domain_id = 1;         /*$$$$ config parameter */
360       a->src_port = 4739;       /*$$$$ config parameter */
361       fm->ipv4_report_created = 1;
362
363       rv = vnet_flow_report_add_del (frm, a);
364       if (rv)
365         {
366           clib_warning ("vnet_flow_report_add_del returned %d", rv);
367           return -1;
368         }
369     }
370   else if (which == FLOW_VARIANT_L2 && !fm->l2_report_created)
371     {
372       memset (a, 0, sizeof (*a));
373       a->rewrite_callback = flowperpkt_template_rewrite_l2;
374       a->flow_data_callback = flowperpkt_data_callback_l2;
375       a->is_add = 1;
376       a->domain_id = 1;         /*$$$$ config parameter */
377       a->src_port = 4739;       /*$$$$ config parameter */
378       fm->l2_report_created = 1;
379
380       rv = vnet_flow_report_add_del (frm, a);
381       if (rv)
382         {
383           clib_warning ("vnet_flow_report_add_del returned %d", rv);
384           return -1;
385         }
386     }
387
388   if (which == FLOW_VARIANT_IPV4)
389     vnet_feature_enable_disable ("ip4-output", "flowperpkt-ipv4",
390                                  sw_if_index, is_add, 0, 0);
391   else if (which == FLOW_VARIANT_L2)
392     vnet_feature_enable_disable ("interface-output", "flowperpkt-l2",
393                                  sw_if_index, is_add, 0, 0);
394
395   return 0;
396 }
397
398 /**
399  * @brief API message handler
400  * @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message
401  */
402 void vl_api_flowperpkt_tx_interface_add_del_t_handler
403   (vl_api_flowperpkt_tx_interface_add_del_t * mp)
404 {
405   flowperpkt_main_t *fm = &flowperpkt_main;
406   vl_api_flowperpkt_tx_interface_add_del_reply_t *rmp;
407   u32 sw_if_index = ntohl (mp->sw_if_index);
408   int rv = 0;
409
410   VALIDATE_SW_IF_INDEX (mp);
411
412   if (mp->which != FLOW_VARIANT_IPV4 && mp->which != FLOW_VARIANT_L2)
413     {
414       rv = VNET_API_ERROR_UNIMPLEMENTED;
415       goto out;
416     }
417
418   rv = flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, mp->which,
419                                                 mp->is_add);
420 out:
421   BAD_SW_IF_INDEX_LABEL;
422
423   REPLY_MACRO (VL_API_FLOWPERPKT_TX_INTERFACE_ADD_DEL_REPLY);
424 }
425
426 /**
427  * @brief API message custom-dump function
428  * @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message
429  * @param handle void * print function handle
430  * @returns u8 * output string
431  */
432 static void *vl_api_flowperpkt_tx_interface_add_del_t_print
433   (vl_api_flowperpkt_tx_interface_add_del_t * mp, void *handle)
434 {
435   u8 *s;
436
437   s = format (0, "SCRIPT: flowperpkt_tx_interface_add_del ");
438   s = format (s, "sw_if_index %d is_add %d which %d ",
439               clib_host_to_net_u32 (mp->sw_if_index),
440               (int) mp->is_add, (int) mp->which);
441   FINISH;
442 }
443
444 /* List of message types that this plugin understands */
445 #define foreach_flowperpkt_plugin_api_msg                           \
446 _(FLOWPERPKT_TX_INTERFACE_ADD_DEL, flowperpkt_tx_interface_add_del)
447
448 /* *INDENT-OFF* */
449 VLIB_PLUGIN_REGISTER () = {
450     .version = VPP_BUILD_VER,
451 };
452 /* *INDENT-ON* */
453
454 static clib_error_t *
455 flowperpkt_tx_interface_add_del_feature_command_fn (vlib_main_t * vm,
456                                                     unformat_input_t * input,
457                                                     vlib_cli_command_t * cmd)
458 {
459   flowperpkt_main_t *fm = &flowperpkt_main;
460   u32 sw_if_index = ~0;
461   int is_add = 1;
462   u8 which = FLOW_VARIANT_IPV4;
463
464   int rv;
465
466   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
467     {
468       if (unformat (input, "disable"))
469         is_add = 0;
470       else if (unformat (input, "%U", unformat_vnet_sw_interface,
471                          fm->vnet_main, &sw_if_index));
472       else if (unformat (input, "l2"))
473         which = FLOW_VARIANT_L2;
474       else
475         break;
476     }
477
478   if (sw_if_index == ~0)
479     return clib_error_return (0, "Please specify an interface...");
480
481   rv =
482     flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, which, is_add);
483   switch (rv)
484     {
485     case 0:
486       break;
487
488     case VNET_API_ERROR_INVALID_SW_IF_INDEX:
489       return clib_error_return
490         (0, "Invalid interface, only works on physical ports");
491       break;
492
493     case VNET_API_ERROR_UNIMPLEMENTED:
494       return clib_error_return (0, "ip6 not supported");
495       break;
496
497     default:
498       return clib_error_return (0, "flowperpkt_enable_disable returned %d",
499                                 rv);
500     }
501   return 0;
502 }
503
504 /*?
505  * '<em>flowperpkt feature add-del</em>' commands to enable/disable
506  * per-packet IPFIX flow record generation on an interface
507  *
508  * @cliexpar
509  * @parblock
510  * To enable per-packet IPFIX flow-record generation on an interface:
511  * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0}
512  *
513  * To disable per-packet IPFIX flow-record generation on an interface:
514  * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0 disable}
515  * @cliexend
516  * @endparblock
517 ?*/
518 /* *INDENT-OFF* */
519 VLIB_CLI_COMMAND (flowperpkt_enable_disable_command, static) = {
520     .path = "flowperpkt feature add-del",
521     .short_help =
522     "flowperpkt feature add-del <interface-name> [disable]",
523     .function = flowperpkt_tx_interface_add_del_feature_command_fn,
524 };
525 /* *INDENT-ON* */
526
527 /**
528  * @brief Set up the API message handling tables
529  * @param vm vlib_main_t * vlib main data structure pointer
530  * @returns 0 to indicate all is well
531  */
532 static clib_error_t *
533 flowperpkt_plugin_api_hookup (vlib_main_t * vm)
534 {
535   flowperpkt_main_t *fm = &flowperpkt_main;
536 #define _(N,n)                                                  \
537     vl_msg_api_set_handlers((VL_API_##N + fm->msg_id_base),     \
538                            #n,                                  \
539                            vl_api_##n##_t_handler,              \
540                            vl_noop_handler,                     \
541                            vl_api_##n##_t_endian,               \
542                            vl_api_##n##_t_print,                \
543                            sizeof(vl_api_##n##_t), 1);
544   foreach_flowperpkt_plugin_api_msg;
545 #undef _
546
547   return 0;
548 }
549
550 #define vl_msg_name_crc_list
551 #include <flowperpkt/flowperpkt_all_api_h.h>
552 #undef vl_msg_name_crc_list
553
554 static void
555 setup_message_id_table (flowperpkt_main_t * fm, api_main_t * am)
556 {
557 #define _(id,n,crc) \
558   vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + fm->msg_id_base);
559   foreach_vl_msg_name_crc_flowperpkt;
560 #undef _
561 }
562
563 /**
564  * @brief Set up the API message handling tables
565  * @param vm vlib_main_t * vlib main data structure pointer
566  * @returns 0 to indicate all is well, or a clib_error_t
567  */
568 static clib_error_t *
569 flowperpkt_init (vlib_main_t * vm)
570 {
571   flowperpkt_main_t *fm = &flowperpkt_main;
572   vlib_thread_main_t *tm = &vlib_thread_main;
573   clib_error_t *error = 0;
574   u32 num_threads;
575   u8 *name;
576
577   fm->vnet_main = vnet_get_main ();
578
579   /* Construct the API name */
580   name = format (0, "flowperpkt_%08x%c", api_version, 0);
581
582   /* Ask for a correctly-sized block of API message decode slots */
583   fm->msg_id_base = vl_msg_api_get_msg_ids
584     ((char *) name, VL_MSG_FIRST_AVAILABLE);
585
586   /* Hook up message handlers */
587   error = flowperpkt_plugin_api_hookup (vm);
588
589   /* Add our API messages to the global name_crc hash table */
590   setup_message_id_table (fm, &api_main);
591
592   vec_free (name);
593
594   /* Decide how many worker threads we have */
595   num_threads = 1 /* main thread */  + tm->n_threads;
596
597   /* Allocate per worker thread vectors */
598   vec_validate (fm->ipv4_buffers_per_worker, num_threads - 1);
599   vec_validate (fm->l2_buffers_per_worker, num_threads - 1);
600   vec_validate (fm->ipv4_frames_per_worker, num_threads - 1);
601   vec_validate (fm->l2_frames_per_worker, num_threads - 1);
602   vec_validate (fm->ipv4_next_record_offset_per_worker, num_threads - 1);
603   vec_validate (fm->l2_next_record_offset_per_worker, num_threads - 1);
604
605   /* Set up time reference pair */
606   fm->vlib_time_0 = vlib_time_now (vm);
607   fm->nanosecond_time_0 = unix_time_now_nsec ();
608
609   return error;
610 }
611
612 VLIB_INIT_FUNCTION (flowperpkt_init);
613
614 /*
615  * fd.io coding-style-patch-verification: ON
616  *
617  * Local Variables:
618  * eval: (c-set-style "gnu")
619  * End:
620  */