2 * flowperpkt.c - per-packet data capture flow report plugin
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:
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 * @brief Per-packet IPFIX flow record generator plugin
22 * This file implements vpp plugin registration mechanics,
23 * debug CLI, and binary API handling.
26 #include <vnet/vnet.h>
27 #include <vnet/plugin/plugin.h>
28 #include <flowperpkt/flowperpkt.h>
30 #include <vlibapi/api.h>
31 #include <vlibmemory/api.h>
32 #include <vlibsocket/api.h>
34 /* define message IDs */
35 #include <flowperpkt/flowperpkt_msg_enum.h>
37 /* define message structures */
39 #include <flowperpkt/flowperpkt_all_api_h.h>
42 /* define generated endian-swappers */
44 #include <flowperpkt/flowperpkt_all_api_h.h>
47 /* instantiate all the print functions we know about */
48 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
50 #include <flowperpkt/flowperpkt_all_api_h.h>
53 flowperpkt_main_t flowperpkt_main;
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>
60 /* Define the per-interface configurable features */
62 VNET_FEATURE_INIT (flow_perpacket_ipv4, static) =
64 .arc_name = "ip4-output",
65 .node_name = "flowperpkt-ipv4",
66 .runs_before = VNET_FEATURES ("interface-output"),
69 VNET_FEATURE_INIT (flow_perpacket_l2, static) =
71 .arc_name = "interface-output",
72 .node_name = "flowperpkt-l2",
73 .runs_before = VNET_FEATURES ("interface-tx"),
78 * A handy macro to set up a message reply.
79 * Assumes that the following variables are available:
80 * mp - pointer to request message
81 * rmp - pointer to reply message type
84 #define REPLY_MACRO(t) \
86 unix_shared_memory_queue_t * q = \
87 vl_api_client_index_to_input_queue (mp->client_index); \
91 rmp = vl_msg_api_alloc (sizeof (*rmp)); \
92 rmp->_vl_msg_id = ntohs((t)+fm->msg_id_base); \
93 rmp->context = mp->context; \
94 rmp->retval = ntohl(rv); \
96 vl_msg_api_send_shmem (q, (u8 *)&rmp); \
99 /* Macro to finish up custom dump fns */
102 vl_print (handle, (char *)s); \
106 #define VALIDATE_SW_IF_INDEX(mp) \
107 do { u32 __sw_if_index = ntohl(mp->sw_if_index); \
108 vnet_main_t *__vnm = vnet_get_main(); \
109 if (pool_is_free_index(__vnm->interface_main.sw_interfaces, \
111 rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; \
112 goto bad_sw_if_index; \
116 #define BAD_SW_IF_INDEX_LABEL \
123 * @brief Create an IPFIX template packet rewrite string
124 * @param frm flow_report_main_t *
125 * @param fr flow_report_t *
126 * @param collector_address ip4_address_t * the IPFIX collector address
127 * @param src_address ip4_address_t * the source address we should use
128 * @param collector_port u16 the collector port we should use, host byte order
129 * @returns u8 * vector containing the indicated IPFIX template packet
132 flowperpkt_template_rewrite_inline (flow_report_main_t * frm,
134 ip4_address_t * collector_address,
135 ip4_address_t * src_address,
136 u16 collector_port, int variant)
140 ipfix_message_header_t *h;
141 ipfix_set_header_t *s;
142 ipfix_template_header_t *t;
143 ipfix_field_specifier_t *f;
144 ipfix_field_specifier_t *first_field;
146 ip4_ipfix_template_packet_t *tp;
148 flow_report_stream_t *stream;
149 flowperpkt_main_t *fm = &flowperpkt_main;
151 stream = &frm->streams[fr->stream_index];
153 if (variant == FLOW_VARIANT_IPV4)
156 * ip4 Supported Fields:
158 * ingressInterface, TLV type 10, u32
159 * egressInterface, TLV type 14, u32
160 * sourceIpv4Address, TLV type 8, u32
161 * destinationIPv4Address, TLV type 12, u32
162 * ipClassOfService, TLV type 5, u8
163 * flowStartNanoseconds, TLV type 156, dateTimeNanoseconds (f64)
164 * Implementation: f64 nanoseconds since VPP started
165 * warning: wireshark doesn't really understand this TLV
166 * dataLinkFrameSize, TLV type 312, u16
167 * warning: wireshark doesn't understand this TLV at all
170 /* Currently 7 fields */
173 /* allocate rewrite space */
176 sizeof (ip4_ipfix_template_packet_t)
177 + field_count * sizeof (ipfix_field_specifier_t) - 1,
178 CLIB_CACHE_LINE_BYTES);
180 else if (variant == FLOW_VARIANT_L2)
183 * L2 Supported Fields:
185 * ingressInterface, TLV type 10, u32
186 * egressInterface, TLV type 14, u32
187 * sourceMacAddress, TLV type 56, u8[6] we hope
188 * destinationMacAddress, TLV type 57, u8[6] we hope
189 * ethernetType, TLV type 256, u16
190 * flowStartNanoseconds, TLV type 156, dateTimeNanoseconds (f64)
191 * Implementation: f64 nanoseconds since VPP started
192 * warning: wireshark doesn't really understand this TLV
193 * dataLinkFrameSize, TLV type 312, u16
194 * warning: wireshark doesn't understand this TLV at all
197 /* Currently 7 fields */
200 /* allocate rewrite space */
203 sizeof (ip4_ipfix_template_packet_t)
204 + field_count * sizeof (ipfix_field_specifier_t) - 1,
205 CLIB_CACHE_LINE_BYTES);
208 tp = (ip4_ipfix_template_packet_t *) rewrite;
209 ip = (ip4_header_t *) & tp->ip4;
210 udp = (udp_header_t *) (ip + 1);
211 h = (ipfix_message_header_t *) (udp + 1);
212 s = (ipfix_set_header_t *) (h + 1);
213 t = (ipfix_template_header_t *) (s + 1);
214 first_field = f = (ipfix_field_specifier_t *) (t + 1);
216 ip->ip_version_and_header_length = 0x45;
218 ip->protocol = IP_PROTOCOL_UDP;
219 ip->src_address.as_u32 = src_address->as_u32;
220 ip->dst_address.as_u32 = collector_address->as_u32;
221 udp->src_port = clib_host_to_net_u16 (stream->src_port);
222 udp->dst_port = clib_host_to_net_u16 (collector_port);
223 udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip));
225 /* FIXUP: message header export_time */
226 /* FIXUP: message header sequence_number */
227 h->domain_id = clib_host_to_net_u32 (stream->domain_id);
229 /* Add TLVs to the template */
230 if (variant == FLOW_VARIANT_IPV4)
233 ipfix_e_id_length (0 /* enterprise */ , ingressInterface,
237 ipfix_e_id_length (0 /* enterprise */ , egressInterface,
241 ipfix_e_id_length (0 /* enterprise */ , sourceIPv4Address,
245 ipfix_e_id_length (0 /* enterprise */ , destinationIPv4Address, 4);
248 ipfix_e_id_length (0 /* enterprise */ , ipClassOfService,
252 ipfix_e_id_length (0 /* enterprise */ , flowStartNanoseconds,
256 ipfix_e_id_length (0 /* enterprise */ , dataLinkFrameSize,
260 else if (variant == FLOW_VARIANT_L2)
263 ipfix_e_id_length (0 /* enterprise */ , ingressInterface,
267 ipfix_e_id_length (0 /* enterprise */ , egressInterface,
271 ipfix_e_id_length (0 /* enterprise */ , sourceMacAddress,
275 ipfix_e_id_length (0 /* enterprise */ , destinationMacAddress, 6);
277 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , ethernetType,
281 ipfix_e_id_length (0 /* enterprise */ , flowStartNanoseconds,
285 ipfix_e_id_length (0 /* enterprise */ , dataLinkFrameSize,
290 /* Extend in the obvious way, right here... */
292 /* Back to the template packet... */
293 ip = (ip4_header_t *) & tp->ip4;
294 udp = (udp_header_t *) (ip + 1);
296 ASSERT (f - first_field);
297 /* Field count in this template */
298 t->id_count = ipfix_id_count (fr->template_id, f - first_field);
300 if (variant == FLOW_VARIANT_IPV4)
301 fm->ipv4_report_id = fr->template_id;
302 else if (variant == FLOW_VARIANT_L2)
303 fm->l2_report_id = fr->template_id;
305 /* set length in octets */
307 ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
309 /* message length in octets */
310 h->version_length = version_length ((u8 *) f - (u8 *) h);
312 ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip);
313 ip->checksum = ip4_header_checksum (ip);
319 flowperpkt_template_rewrite_ipv4 (flow_report_main_t * frm,
321 ip4_address_t * collector_address,
322 ip4_address_t * src_address,
325 return flowperpkt_template_rewrite_inline
326 (frm, fr, collector_address, src_address, collector_port,
331 flowperpkt_template_rewrite_l2 (flow_report_main_t * frm,
333 ip4_address_t * collector_address,
334 ip4_address_t * src_address,
337 return flowperpkt_template_rewrite_inline
338 (frm, fr, collector_address, src_address, collector_port,
344 * @brief Flush accumulated data
345 * @param frm flow_report_main_t *
346 * @param fr flow_report_t *
347 * @param f vlib_frame_t *
350 * This function must simply return the incoming frame, or no template packets
354 flowperpkt_data_callback_ipv4 (flow_report_main_t * frm,
356 vlib_frame_t * f, u32 * to_next,
359 flowperpkt_flush_callback_ipv4 ();
364 flowperpkt_data_callback_l2 (flow_report_main_t * frm,
366 vlib_frame_t * f, u32 * to_next, u32 node_index)
368 flowperpkt_flush_callback_l2 ();
373 * @brief configure / deconfigure the IPFIX flow-per-packet
374 * @param fm flowperpkt_main_t * fm
375 * @param sw_if_index u32 the desired interface
376 * @param is_add int 1 to enable the feature, 0 to disable it
377 * @returns 0 if successful, non-zero otherwise
380 static int flowperpkt_tx_interface_add_del_feature
381 (flowperpkt_main_t * fm, u32 sw_if_index, int which, int is_add)
383 flow_report_main_t *frm = &flow_report_main;
384 vnet_flow_report_add_del_args_t _a, *a = &_a;
387 if (which == FLOW_VARIANT_IPV4 && !fm->ipv4_report_created)
389 memset (a, 0, sizeof (*a));
390 a->rewrite_callback = flowperpkt_template_rewrite_ipv4;
391 a->flow_data_callback = flowperpkt_data_callback_ipv4;
393 a->domain_id = 1; /*$$$$ config parameter */
394 a->src_port = 4739; /*$$$$ config parameter */
395 fm->ipv4_report_created = 1;
397 rv = vnet_flow_report_add_del (frm, a);
400 clib_warning ("vnet_flow_report_add_del returned %d", rv);
404 else if (which == FLOW_VARIANT_L2 && !fm->l2_report_created)
406 memset (a, 0, sizeof (*a));
407 a->rewrite_callback = flowperpkt_template_rewrite_l2;
408 a->flow_data_callback = flowperpkt_data_callback_l2;
410 a->domain_id = 1; /*$$$$ config parameter */
411 a->src_port = 4739; /*$$$$ config parameter */
412 fm->l2_report_created = 1;
414 rv = vnet_flow_report_add_del (frm, a);
417 clib_warning ("vnet_flow_report_add_del returned %d", rv);
422 if (which == FLOW_VARIANT_IPV4)
423 vnet_feature_enable_disable ("ip4-output", "flowperpkt-ipv4",
424 sw_if_index, is_add, 0, 0);
425 else if (which == FLOW_VARIANT_L2)
426 vnet_feature_enable_disable ("interface-output", "flowperpkt-l2",
427 sw_if_index, is_add, 0, 0);
433 * @brief API message handler
434 * @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message
436 void vl_api_flowperpkt_tx_interface_add_del_t_handler
437 (vl_api_flowperpkt_tx_interface_add_del_t * mp)
439 flowperpkt_main_t *fm = &flowperpkt_main;
440 vl_api_flowperpkt_tx_interface_add_del_reply_t *rmp;
441 u32 sw_if_index = ntohl (mp->sw_if_index);
444 VALIDATE_SW_IF_INDEX (mp);
446 if (mp->which != FLOW_VARIANT_IPV4 && mp->which != FLOW_VARIANT_L2)
448 rv = VNET_API_ERROR_UNIMPLEMENTED;
452 rv = flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, mp->which,
455 BAD_SW_IF_INDEX_LABEL;
457 REPLY_MACRO (VL_API_FLOWPERPKT_TX_INTERFACE_ADD_DEL_REPLY);
461 * @brief API message custom-dump function
462 * @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message
463 * @param handle void * print function handle
464 * @returns u8 * output string
466 static void *vl_api_flowperpkt_tx_interface_add_del_t_print
467 (vl_api_flowperpkt_tx_interface_add_del_t * mp, void *handle)
471 s = format (0, "SCRIPT: flowperpkt_tx_interface_add_del ");
472 s = format (s, "sw_if_index %d is_add %d which %d ",
473 clib_host_to_net_u32 (mp->sw_if_index),
474 (int) mp->is_add, (int) mp->which);
478 /* List of message types that this plugin understands */
479 #define foreach_flowperpkt_plugin_api_msg \
480 _(FLOWPERPKT_TX_INTERFACE_ADD_DEL, flowperpkt_tx_interface_add_del)
483 * @brief plugin-api required function
484 * @param vm vlib_main_t * vlib main data structure pointer
485 * @param h vlib_plugin_handoff_t * handoff structure
486 * @param from_early_init int notused
489 * This routine exists to convince the vlib plugin framework that
490 * we haven't accidentally copied a random .dll into the plugin directory.
492 * Also collects global variable pointers passed from the vpp engine
495 vlib_plugin_register (vlib_main_t * vm, vnet_plugin_handoff_t * h,
498 flowperpkt_main_t *fm = &flowperpkt_main;
499 clib_error_t *error = 0;
502 fm->vnet_main = h->vnet_main;
507 static clib_error_t *
508 flowperpkt_tx_interface_add_del_feature_command_fn (vlib_main_t * vm,
509 unformat_input_t * input,
510 vlib_cli_command_t * cmd)
512 flowperpkt_main_t *fm = &flowperpkt_main;
513 u32 sw_if_index = ~0;
515 u8 which = FLOW_VARIANT_IPV4;
519 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
521 if (unformat (input, "disable"))
523 else if (unformat (input, "%U", unformat_vnet_sw_interface,
524 fm->vnet_main, &sw_if_index));
525 else if (unformat (input, "l2"))
526 which = FLOW_VARIANT_L2;
531 if (sw_if_index == ~0)
532 return clib_error_return (0, "Please specify an interface...");
535 flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, which, is_add);
541 case VNET_API_ERROR_INVALID_SW_IF_INDEX:
542 return clib_error_return
543 (0, "Invalid interface, only works on physical ports");
546 case VNET_API_ERROR_UNIMPLEMENTED:
547 return clib_error_return (0, "ip6 not supported");
551 return clib_error_return (0, "flowperpkt_enable_disable returned %d",
558 * '<em>flowperpkt feature add-del</em>' commands to enable/disable
559 * per-packet IPFIX flow record generation on an interface
563 * To enable per-packet IPFIX flow-record generation on an interface:
564 * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0}
566 * To disable per-packet IPFIX flow-record generation on an interface:
567 * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0 disable}
572 VLIB_CLI_COMMAND (flowperpkt_enable_disable_command, static) = {
573 .path = "flowperpkt feature add-del",
575 "flowperpkt feature add-del <interface-name> [disable]",
576 .function = flowperpkt_tx_interface_add_del_feature_command_fn,
581 * @brief Set up the API message handling tables
582 * @param vm vlib_main_t * vlib main data structure pointer
583 * @returns 0 to indicate all is well
585 static clib_error_t *
586 flowperpkt_plugin_api_hookup (vlib_main_t * vm)
588 flowperpkt_main_t *fm = &flowperpkt_main;
590 vl_msg_api_set_handlers((VL_API_##N + fm->msg_id_base), \
592 vl_api_##n##_t_handler, \
594 vl_api_##n##_t_endian, \
595 vl_api_##n##_t_print, \
596 sizeof(vl_api_##n##_t), 1);
597 foreach_flowperpkt_plugin_api_msg;
603 #define vl_msg_name_crc_list
604 #include <flowperpkt/flowperpkt_all_api_h.h>
605 #undef vl_msg_name_crc_list
608 setup_message_id_table (flowperpkt_main_t * fm, api_main_t * am)
610 #define _(id,n,crc) \
611 vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + fm->msg_id_base);
612 foreach_vl_msg_name_crc_flowperpkt;
617 * @brief Set up the API message handling tables
618 * @param vm vlib_main_t * vlib main data structure pointer
619 * @returns 0 to indicate all is well, or a clib_error_t
621 static clib_error_t *
622 flowperpkt_init (vlib_main_t * vm)
624 flowperpkt_main_t *fm = &flowperpkt_main;
625 vlib_thread_main_t *tm = &vlib_thread_main;
626 clib_error_t *error = 0;
630 /* Construct the API name */
631 name = format (0, "flowperpkt_%08x%c", api_version, 0);
633 /* Ask for a correctly-sized block of API message decode slots */
634 fm->msg_id_base = vl_msg_api_get_msg_ids
635 ((char *) name, VL_MSG_FIRST_AVAILABLE);
637 /* Hook up message handlers */
638 error = flowperpkt_plugin_api_hookup (vm);
640 /* Add our API messages to the global name_crc hash table */
641 setup_message_id_table (fm, &api_main);
645 /* Decide how many worker threads we have */
646 num_threads = 1 /* main thread */ + tm->n_threads;
648 /* Allocate per worker thread vectors */
649 vec_validate (fm->ipv4_buffers_per_worker, num_threads - 1);
650 vec_validate (fm->l2_buffers_per_worker, num_threads - 1);
651 vec_validate (fm->ipv4_frames_per_worker, num_threads - 1);
652 vec_validate (fm->l2_frames_per_worker, num_threads - 1);
653 vec_validate (fm->ipv4_next_record_offset_per_worker, num_threads - 1);
654 vec_validate (fm->l2_next_record_offset_per_worker, num_threads - 1);
656 /* Set up time reference pair */
657 fm->vlib_time_0 = vlib_time_now (vm);
658 fm->nanosecond_time_0 = unix_time_now_nsec ();
663 VLIB_INIT_FUNCTION (flowperpkt_init);
666 * fd.io coding-style-patch-verification: ON
669 * eval: (c-set-style "gnu")