dbdb189865d4d7f568e93e59473e94b64cf1a68b
[vpp.git] / plugins / flowperpkt-plugin / 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 <vnet/plugin/plugin.h>
28 #include <flowperpkt/flowperpkt.h>
29
30 #include <vlibapi/api.h>
31 #include <vlibmemory/api.h>
32 #include <vlibsocket/api.h>
33
34 /* define message IDs */
35 #include <flowperpkt/flowperpkt_msg_enum.h>
36
37 /* define message structures */
38 #define vl_typedefs
39 #include <flowperpkt/flowperpkt_all_api_h.h>
40 #undef vl_typedefs
41
42 /* define generated endian-swappers */
43 #define vl_endianfun
44 #include <flowperpkt/flowperpkt_all_api_h.h>
45 #undef vl_endianfun
46
47 /* instantiate all the print functions we know about */
48 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
49 #define vl_printfun
50 #include <flowperpkt/flowperpkt_all_api_h.h>
51 #undef vl_printfun
52
53 flowperpkt_main_t flowperpkt_main;
54
55 /* Get the API version number */
56 #define vl_api_version(n,v) static u32 api_version=(v);
57 #include <flowperpkt/flowperpkt_all_api_h.h>
58 #undef vl_api_version
59
60 /* Define the per-interface configurable feature */
61 /* *INDENT-OFF* */
62 VNET_FEATURE_INIT (flow_perpacket, static) = {
63   .arc_name = "ip4-output",
64   .node_name = "flowperpkt",
65   .runs_before = VNET_FEATURES ("interface-output"),
66 };
67 /* *INDENT-ON* */
68
69 /*
70  * A handy macro to set up a message reply.
71  * Assumes that the following variables are available:
72  * mp - pointer to request message
73  * rmp - pointer to reply message type
74  * rv - return value
75  */
76 #define REPLY_MACRO(t)                                          \
77 do {                                                            \
78     unix_shared_memory_queue_t * q =                            \
79     vl_api_client_index_to_input_queue (mp->client_index);      \
80     if (!q)                                                     \
81         return;                                                 \
82                                                                 \
83     rmp = vl_msg_api_alloc (sizeof (*rmp));                     \
84     rmp->_vl_msg_id = ntohs((t)+fm->msg_id_base);               \
85     rmp->context = mp->context;                                 \
86     rmp->retval = ntohl(rv);                                    \
87                                                                 \
88     vl_msg_api_send_shmem (q, (u8 *)&rmp);                      \
89 } while(0);
90
91 /* Macro to finish up custom dump fns */
92 #define FINISH                                  \
93     vec_add1 (s, 0);                            \
94     vl_print (handle, (char *)s);               \
95     vec_free (s);                               \
96     return handle;
97
98 #define VALIDATE_SW_IF_INDEX(mp)                                \
99  do { u32 __sw_if_index = ntohl(mp->sw_if_index);               \
100     vnet_main_t *__vnm = vnet_get_main();                       \
101     if (pool_is_free_index(__vnm->interface_main.sw_interfaces, \
102                            __sw_if_index)) {                    \
103         rv = VNET_API_ERROR_INVALID_SW_IF_INDEX;                \
104         goto bad_sw_if_index;                                   \
105     }                                                           \
106 } while(0);
107
108 #define BAD_SW_IF_INDEX_LABEL                   \
109 do {                                            \
110 bad_sw_if_index:                                \
111     ;                                           \
112 } while (0);
113
114 /**
115  * @brief Create an IPFIX template packet rewrite string
116  * @param frm flow_report_main_t *
117  * @param fr flow_report_t *
118  * @param collector_address ip4_address_t * the IPFIX collector address
119  * @param src_address ip4_address_t * the source address we should use
120  * @param collector_port u16 the collector port we should use, host byte order
121  * @returns u8 * vector containing the indicated IPFIX template packet
122  */
123 u8 *
124 flowperpkt_template_rewrite (flow_report_main_t * frm,
125                              flow_report_t * fr,
126                              ip4_address_t * collector_address,
127                              ip4_address_t * src_address, u16 collector_port)
128 {
129   ip4_header_t *ip;
130   udp_header_t *udp;
131   ipfix_message_header_t *h;
132   ipfix_set_header_t *s;
133   ipfix_template_header_t *t;
134   ipfix_field_specifier_t *f;
135   ipfix_field_specifier_t *first_field;
136   u8 *rewrite = 0;
137   ip4_ipfix_template_packet_t *tp;
138   u32 field_count = 0;
139   flow_report_stream_t *stream;
140
141   stream = &frm->streams[fr->stream_index];
142
143   /*
144    * Supported Fields:
145    *
146    * egressInterface, TLV type 14, u32
147    * ipClassOfService, TLV type 5, u8
148    * flowStartNanoseconds, TLV type 156, dateTimeNanoseconds (f64)
149    *   Implementation: f64 nanoseconds since VPP started
150    *   warning: wireshark doesn't really understand this TLV
151    * dataLinkFrameSize, TLV type 312, u16
152    *   warning: wireshark doesn't understand this TLV at all
153    */
154
155   /* Currently 4 fields */
156   field_count += 4;
157
158   /* allocate rewrite space */
159   vec_validate_aligned (rewrite,
160                         sizeof (ip4_ipfix_template_packet_t)
161                         + field_count * sizeof (ipfix_field_specifier_t) - 1,
162                         CLIB_CACHE_LINE_BYTES);
163
164   tp = (ip4_ipfix_template_packet_t *) rewrite;
165   ip = (ip4_header_t *) & tp->ip4;
166   udp = (udp_header_t *) (ip + 1);
167   h = (ipfix_message_header_t *) (udp + 1);
168   s = (ipfix_set_header_t *) (h + 1);
169   t = (ipfix_template_header_t *) (s + 1);
170   first_field = f = (ipfix_field_specifier_t *) (t + 1);
171
172   ip->ip_version_and_header_length = 0x45;
173   ip->ttl = 254;
174   ip->protocol = IP_PROTOCOL_UDP;
175   ip->src_address.as_u32 = src_address->as_u32;
176   ip->dst_address.as_u32 = collector_address->as_u32;
177   udp->src_port = clib_host_to_net_u16 (stream->src_port);
178   udp->dst_port = clib_host_to_net_u16 (collector_port);
179   udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip));
180
181   /* FIXUP: message header export_time */
182   /* FIXUP: message header sequence_number */
183   h->domain_id = clib_host_to_net_u32 (stream->domain_id);
184
185   /* Add TLVs to the template */
186   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , egressInterface,
187                                       4);
188   f++;
189   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , ipClassOfService,
190                                       1);
191   f++;
192   f->e_id_length =
193     ipfix_e_id_length (0 /* enterprise */ , flowStartNanoseconds,
194                        8);
195   f++;
196   f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , dataLinkFrameSize,
197                                       2);
198   f++;
199   /* Extend in the obvious way, right here... */
200
201   /* Back to the template packet... */
202   ip = (ip4_header_t *) & tp->ip4;
203   udp = (udp_header_t *) (ip + 1);
204
205   ASSERT (f - first_field);
206   /* Field count in this template */
207   t->id_count = ipfix_id_count (fr->template_id, f - first_field);
208
209   /* set length in octets */
210   s->set_id_length =
211     ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
212
213   /* message length in octets */
214   h->version_length = version_length ((u8 *) f - (u8 *) h);
215
216   ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip);
217   ip->checksum = ip4_header_checksum (ip);
218
219   return rewrite;
220 }
221
222 /**
223  * @brief Flush accumulated data
224  * @param frm flow_report_main_t *
225  * @param fr flow_report_t *
226  * @param f vlib_frame_t *
227  *
228  * <em>Notes:</em>
229  * This function must simply return the incoming frame, or no template packets
230  * will be sent.
231  */
232 vlib_frame_t *
233 flowperpkt_data_callback (flow_report_main_t * frm,
234                           flow_report_t * fr,
235                           vlib_frame_t * f, u32 * to_next, u32 node_index)
236 {
237   flowperpkt_flush_callback ();
238   return f;
239 }
240
241 /**
242  * @brief configure / deconfigure the IPFIX flow-per-packet
243  * @param fm flowperpkt_main_t * fm
244  * @param sw_if_index u32 the desired interface
245  * @param is_add int 1 to enable the feature, 0 to disable it
246  * @returns 0 if successful, non-zero otherwise
247  */
248
249 static int flowperpkt_tx_interface_add_del_feature
250   (flowperpkt_main_t * fm, u32 sw_if_index, int is_add)
251 {
252   flow_report_main_t *frm = &flow_report_main;
253   vnet_flow_report_add_del_args_t _a, *a = &_a;
254   int rv;
255
256   if (!fm->report_created)
257     {
258       memset (a, 0, sizeof (*a));
259       a->rewrite_callback = flowperpkt_template_rewrite;
260       a->flow_data_callback = flowperpkt_data_callback;
261       a->is_add = 1;
262       a->domain_id = 1;         /*$$$$ config parameter */
263       a->src_port = 4739;       /*$$$$ config parameter */
264       fm->report_created = 1;
265
266       rv = vnet_flow_report_add_del (frm, a);
267       if (rv)
268         {
269           clib_warning ("vnet_flow_report_add_del returned %d", rv);
270           return -1;
271         }
272     }
273
274   vnet_feature_enable_disable ("ip4-output", "flowperpkt", sw_if_index,
275                                is_add, 0, 0);
276
277   return 0;
278 }
279
280 /**
281  * @brief API message handler
282  * @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message
283  */
284 void vl_api_flowperpkt_tx_interface_add_del_t_handler
285   (vl_api_flowperpkt_tx_interface_add_del_t * mp)
286 {
287   flowperpkt_main_t *fm = &flowperpkt_main;
288   vl_api_flowperpkt_tx_interface_add_del_reply_t *rmp;
289   u32 sw_if_index = ntohl (mp->sw_if_index);
290   int rv = 0;
291
292   VALIDATE_SW_IF_INDEX (mp);
293
294   rv = flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, mp->is_add);
295
296   BAD_SW_IF_INDEX_LABEL;
297
298   REPLY_MACRO (VL_API_FLOWPERPKT_TX_INTERFACE_ADD_DEL_REPLY);
299 }
300
301 /**
302  * @brief API message custom-dump function
303  * @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message
304  * @param handle void * print function handle
305  * @returns u8 * output string
306  */
307 static void *vl_api_flowperpkt_tx_interface_add_del_t_print
308   (vl_api_flowperpkt_tx_interface_add_del_t * mp, void *handle)
309 {
310   u8 *s;
311
312   s = format (0, "SCRIPT: flowperpkt_tx_interface_add_del ");
313   s = format (s, "sw_if_index %d is_add %d is_ipv6 %d ",
314               clib_host_to_net_u32 (mp->sw_if_index),
315               (int) mp->is_add, (int) mp->is_ipv6);
316   FINISH;
317 }
318
319 /* List of message types that this plugin understands */
320 #define foreach_flowperpkt_plugin_api_msg                           \
321 _(FLOWPERPKT_TX_INTERFACE_ADD_DEL, flowperpkt_tx_interface_add_del)
322
323 /**
324  * @brief plugin-api required function
325  * @param vm vlib_main_t * vlib main data structure pointer
326  * @param h vlib_plugin_handoff_t * handoff structure
327  * @param from_early_init int notused
328  *
329  * <em>Notes:</em>
330  * This routine exists to convince the vlib plugin framework that
331  * we haven't accidentally copied a random .dll into the plugin directory.
332  *
333  * Also collects global variable pointers passed from the vpp engine
334  */
335 clib_error_t *
336 vlib_plugin_register (vlib_main_t * vm, vnet_plugin_handoff_t * h,
337                       int from_early_init)
338 {
339   flowperpkt_main_t *fm = &flowperpkt_main;
340   clib_error_t *error = 0;
341
342   fm->vlib_main = vm;
343   fm->vnet_main = h->vnet_main;
344
345   return error;
346 }
347
348 static clib_error_t *
349 flowperpkt_tx_interface_add_del_feature_command_fn (vlib_main_t * vm,
350                                                     unformat_input_t * input,
351                                                     vlib_cli_command_t * cmd)
352 {
353   flowperpkt_main_t *fm = &flowperpkt_main;
354   u32 sw_if_index = ~0;
355   int is_add = 1;
356
357   int rv;
358
359   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
360     {
361       if (unformat (input, "disable"))
362         is_add = 0;
363       else if (unformat (input, "%U", unformat_vnet_sw_interface,
364                          fm->vnet_main, &sw_if_index))
365         ;
366       else
367         break;
368     }
369
370   if (sw_if_index == ~0)
371     return clib_error_return (0, "Please specify an interface...");
372
373   rv = flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, is_add);
374   switch (rv)
375     {
376     case 0:
377       break;
378
379     case VNET_API_ERROR_INVALID_SW_IF_INDEX:
380       return clib_error_return
381         (0, "Invalid interface, only works on physical ports");
382       break;
383
384     case VNET_API_ERROR_UNIMPLEMENTED:
385       return clib_error_return (0, "ip6 not supported");
386       break;
387
388     default:
389       return clib_error_return (0, "flowperpkt_enable_disable returned %d",
390                                 rv);
391     }
392   return 0;
393 }
394
395 /*?
396  * '<em>flowperpkt feature add-del</em>' commands to enable/disable
397  * per-packet IPFIX flow record generation on an interface
398  *
399  * @cliexpar
400  * @parblock
401  * To enable per-packet IPFIX flow-record generation on an interface:
402  * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0}
403  *
404  * To disable per-packet IPFIX flow-record generation on an interface:
405  * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0 disable}
406  * @cliexend
407  * @endparblock
408 ?*/
409 /* *INDENT-OFF* */
410 VLIB_CLI_COMMAND (flowperpkt_enable_disable_command, static) = {
411     .path = "flowperpkt feature add-del",
412     .short_help =
413     "flowperpkt feature add-del <interface-name> [disable]",
414     .function = flowperpkt_tx_interface_add_del_feature_command_fn,
415 };
416 /* *INDENT-ON* */
417
418 /**
419  * @brief Set up the API message handling tables
420  * @param vm vlib_main_t * vlib main data structure pointer
421  * @returns 0 to indicate all is well
422  */
423 static clib_error_t *
424 flowperpkt_plugin_api_hookup (vlib_main_t * vm)
425 {
426   flowperpkt_main_t *fm = &flowperpkt_main;
427 #define _(N,n)                                                  \
428     vl_msg_api_set_handlers((VL_API_##N + fm->msg_id_base),     \
429                            #n,                                  \
430                            vl_api_##n##_t_handler,              \
431                            vl_noop_handler,                     \
432                            vl_api_##n##_t_endian,               \
433                            vl_api_##n##_t_print,                \
434                            sizeof(vl_api_##n##_t), 1);
435   foreach_flowperpkt_plugin_api_msg;
436 #undef _
437
438   return 0;
439 }
440
441 /**
442  * @brief Set up the API message handling tables
443  * @param vm vlib_main_t * vlib main data structure pointer
444  * @returns 0 to indicate all is well, or a clib_error_t
445  */
446 static clib_error_t *
447 flowperpkt_init (vlib_main_t * vm)
448 {
449   flowperpkt_main_t *fm = &flowperpkt_main;
450   vlib_thread_main_t *tm = &vlib_thread_main;
451   clib_error_t *error = 0;
452   u32 num_threads;
453   u8 *name;
454
455   /* Construct the API name */
456   name = format (0, "flowperpkt_%08x%c", api_version, 0);
457
458   /* Ask for a correctly-sized block of API message decode slots */
459   fm->msg_id_base = vl_msg_api_get_msg_ids
460     ((char *) name, VL_MSG_FIRST_AVAILABLE);
461
462   /* Hook up message handlers */
463   error = flowperpkt_plugin_api_hookup (vm);
464
465   vec_free (name);
466
467   /* Decide how many worker threads we have */
468   num_threads = 1 /* main thread */  + tm->n_eal_threads;
469
470   /* Allocate per worker thread vectors */
471   vec_validate (fm->buffers_per_worker, num_threads - 1);
472   vec_validate (fm->frames_per_worker, num_threads - 1);
473   vec_validate (fm->next_record_offset_per_worker, num_threads - 1);
474
475   /* Set up time reference pair */
476   fm->vlib_time_0 = vlib_time_now (vm);
477   fm->nanosecond_time_0 = unix_time_now_nsec ();
478
479   return error;
480 }
481
482 VLIB_INIT_FUNCTION (flowperpkt_init);
483
484 /*
485  * fd.io coding-style-patch-verification: ON
486  *
487  * Local Variables:
488  * eval: (c-set-style "gnu")
489  * End:
490  */