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 <vpp/app/version.h>
28 #include <vnet/plugin/plugin.h>
29 #include <flowperpkt/flowperpkt.h>
31 #include <vlibapi/api.h>
32 #include <vlibmemory/api.h>
33 #include <vlibsocket/api.h>
35 /* define message IDs */
36 #include <flowperpkt/flowperpkt_msg_enum.h>
38 /* define message structures */
40 #include <flowperpkt/flowperpkt_all_api_h.h>
43 /* define generated endian-swappers */
45 #include <flowperpkt/flowperpkt_all_api_h.h>
48 /* instantiate all the print functions we know about */
49 #define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__)
51 #include <flowperpkt/flowperpkt_all_api_h.h>
54 flowperpkt_main_t flowperpkt_main;
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>
61 #define REPLY_MSG_ID_BASE fm->msg_id_base
62 #include <vlibapi/api_helper_macros.h>
64 /* Define the per-interface configurable features */
66 VNET_FEATURE_INIT (flow_perpacket_ipv4, static) =
68 .arc_name = "ip4-output",
69 .node_name = "flowperpkt-ipv4",
70 .runs_before = VNET_FEATURES ("interface-output"),
73 VNET_FEATURE_INIT (flow_perpacket_l2, static) =
75 .arc_name = "interface-output",
76 .node_name = "flowperpkt-l2",
77 .runs_before = VNET_FEATURES ("interface-tx"),
81 /* Macro to finish up custom dump fns */
84 vl_print (handle, (char *)s); \
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
98 flowperpkt_template_rewrite_inline (flow_report_main_t * frm,
100 ip4_address_t * collector_address,
101 ip4_address_t * src_address,
102 u16 collector_port, int variant)
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;
112 ip4_ipfix_template_packet_t *tp;
114 flow_report_stream_t *stream;
115 flowperpkt_main_t *fm = &flowperpkt_main;
117 stream = &frm->streams[fr->stream_index];
119 if (variant == FLOW_VARIANT_IPV4)
122 * ip4 Supported Fields:
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
136 /* Currently 7 fields */
139 /* allocate rewrite space */
142 sizeof (ip4_ipfix_template_packet_t)
143 + field_count * sizeof (ipfix_field_specifier_t) - 1,
144 CLIB_CACHE_LINE_BYTES);
146 else if (variant == FLOW_VARIANT_L2)
149 * L2 Supported Fields:
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
163 /* Currently 7 fields */
166 /* allocate rewrite space */
169 sizeof (ip4_ipfix_template_packet_t)
170 + field_count * sizeof (ipfix_field_specifier_t) - 1,
171 CLIB_CACHE_LINE_BYTES);
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);
182 ip->ip_version_and_header_length = 0x45;
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));
191 /* FIXUP: message header export_time */
192 /* FIXUP: message header sequence_number */
193 h->domain_id = clib_host_to_net_u32 (stream->domain_id);
195 /* Add TLVs to the template */
196 if (variant == FLOW_VARIANT_IPV4)
199 ipfix_e_id_length (0 /* enterprise */ , ingressInterface,
203 ipfix_e_id_length (0 /* enterprise */ , egressInterface,
207 ipfix_e_id_length (0 /* enterprise */ , sourceIPv4Address,
211 ipfix_e_id_length (0 /* enterprise */ , destinationIPv4Address, 4);
214 ipfix_e_id_length (0 /* enterprise */ , ipClassOfService,
218 ipfix_e_id_length (0 /* enterprise */ , flowStartNanoseconds,
222 ipfix_e_id_length (0 /* enterprise */ , dataLinkFrameSize,
226 else if (variant == FLOW_VARIANT_L2)
229 ipfix_e_id_length (0 /* enterprise */ , ingressInterface,
233 ipfix_e_id_length (0 /* enterprise */ , egressInterface,
237 ipfix_e_id_length (0 /* enterprise */ , sourceMacAddress,
241 ipfix_e_id_length (0 /* enterprise */ , destinationMacAddress, 6);
243 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , ethernetType,
247 ipfix_e_id_length (0 /* enterprise */ , flowStartNanoseconds,
251 ipfix_e_id_length (0 /* enterprise */ , dataLinkFrameSize,
256 /* Extend in the obvious way, right here... */
258 /* Back to the template packet... */
259 ip = (ip4_header_t *) & tp->ip4;
260 udp = (udp_header_t *) (ip + 1);
262 ASSERT (f - first_field);
263 /* Field count in this template */
264 t->id_count = ipfix_id_count (fr->template_id, f - first_field);
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;
271 /* set length in octets */
273 ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
275 /* message length in octets */
276 h->version_length = version_length ((u8 *) f - (u8 *) h);
278 ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip);
279 ip->checksum = ip4_header_checksum (ip);
285 flowperpkt_template_rewrite_ipv4 (flow_report_main_t * frm,
287 ip4_address_t * collector_address,
288 ip4_address_t * src_address,
291 return flowperpkt_template_rewrite_inline
292 (frm, fr, collector_address, src_address, collector_port,
297 flowperpkt_template_rewrite_l2 (flow_report_main_t * frm,
299 ip4_address_t * collector_address,
300 ip4_address_t * src_address,
303 return flowperpkt_template_rewrite_inline
304 (frm, fr, collector_address, src_address, collector_port,
310 * @brief Flush accumulated data
311 * @param frm flow_report_main_t *
312 * @param fr flow_report_t *
313 * @param f vlib_frame_t *
316 * This function must simply return the incoming frame, or no template packets
320 flowperpkt_data_callback_ipv4 (flow_report_main_t * frm,
322 vlib_frame_t * f, u32 * to_next,
325 flowperpkt_flush_callback_ipv4 ();
330 flowperpkt_data_callback_l2 (flow_report_main_t * frm,
332 vlib_frame_t * f, u32 * to_next, u32 node_index)
334 flowperpkt_flush_callback_l2 ();
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
346 static int flowperpkt_tx_interface_add_del_feature
347 (flowperpkt_main_t * fm, u32 sw_if_index, int which, int is_add)
349 flow_report_main_t *frm = &flow_report_main;
350 vnet_flow_report_add_del_args_t _a, *a = &_a;
353 if (which == FLOW_VARIANT_IPV4 && !fm->ipv4_report_created)
355 memset (a, 0, sizeof (*a));
356 a->rewrite_callback = flowperpkt_template_rewrite_ipv4;
357 a->flow_data_callback = flowperpkt_data_callback_ipv4;
359 a->domain_id = 1; /*$$$$ config parameter */
360 a->src_port = 4739; /*$$$$ config parameter */
361 fm->ipv4_report_created = 1;
363 rv = vnet_flow_report_add_del (frm, a);
366 clib_warning ("vnet_flow_report_add_del returned %d", rv);
370 else if (which == FLOW_VARIANT_L2 && !fm->l2_report_created)
372 memset (a, 0, sizeof (*a));
373 a->rewrite_callback = flowperpkt_template_rewrite_l2;
374 a->flow_data_callback = flowperpkt_data_callback_l2;
376 a->domain_id = 1; /*$$$$ config parameter */
377 a->src_port = 4739; /*$$$$ config parameter */
378 fm->l2_report_created = 1;
380 rv = vnet_flow_report_add_del (frm, a);
383 clib_warning ("vnet_flow_report_add_del returned %d", rv);
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);
399 * @brief API message handler
400 * @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message
402 void vl_api_flowperpkt_tx_interface_add_del_t_handler
403 (vl_api_flowperpkt_tx_interface_add_del_t * mp)
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);
410 VALIDATE_SW_IF_INDEX (mp);
412 if (mp->which != FLOW_VARIANT_IPV4 && mp->which != FLOW_VARIANT_L2)
414 rv = VNET_API_ERROR_UNIMPLEMENTED;
418 rv = flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, mp->which,
421 BAD_SW_IF_INDEX_LABEL;
423 REPLY_MACRO (VL_API_FLOWPERPKT_TX_INTERFACE_ADD_DEL_REPLY);
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
432 static void *vl_api_flowperpkt_tx_interface_add_del_t_print
433 (vl_api_flowperpkt_tx_interface_add_del_t * mp, void *handle)
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);
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)
449 VLIB_PLUGIN_REGISTER () = {
450 .version = VPP_BUILD_VER,
451 .description = "Flow per Packet",
455 static clib_error_t *
456 flowperpkt_tx_interface_add_del_feature_command_fn (vlib_main_t * vm,
457 unformat_input_t * input,
458 vlib_cli_command_t * cmd)
460 flowperpkt_main_t *fm = &flowperpkt_main;
461 u32 sw_if_index = ~0;
463 u8 which = FLOW_VARIANT_IPV4;
467 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
469 if (unformat (input, "disable"))
471 else if (unformat (input, "%U", unformat_vnet_sw_interface,
472 fm->vnet_main, &sw_if_index));
473 else if (unformat (input, "l2"))
474 which = FLOW_VARIANT_L2;
479 if (sw_if_index == ~0)
480 return clib_error_return (0, "Please specify an interface...");
483 flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, which, is_add);
489 case VNET_API_ERROR_INVALID_SW_IF_INDEX:
490 return clib_error_return
491 (0, "Invalid interface, only works on physical ports");
494 case VNET_API_ERROR_UNIMPLEMENTED:
495 return clib_error_return (0, "ip6 not supported");
499 return clib_error_return (0, "flowperpkt_enable_disable returned %d",
506 * '<em>flowperpkt feature add-del</em>' commands to enable/disable
507 * per-packet IPFIX flow record generation on an interface
511 * To enable per-packet IPFIX flow-record generation on an interface:
512 * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0}
514 * To disable per-packet IPFIX flow-record generation on an interface:
515 * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0 disable}
520 VLIB_CLI_COMMAND (flowperpkt_enable_disable_command, static) = {
521 .path = "flowperpkt feature add-del",
523 "flowperpkt feature add-del <interface-name> [disable]",
524 .function = flowperpkt_tx_interface_add_del_feature_command_fn,
529 * @brief Set up the API message handling tables
530 * @param vm vlib_main_t * vlib main data structure pointer
531 * @returns 0 to indicate all is well
533 static clib_error_t *
534 flowperpkt_plugin_api_hookup (vlib_main_t * vm)
536 flowperpkt_main_t *fm = &flowperpkt_main;
538 vl_msg_api_set_handlers((VL_API_##N + fm->msg_id_base), \
540 vl_api_##n##_t_handler, \
542 vl_api_##n##_t_endian, \
543 vl_api_##n##_t_print, \
544 sizeof(vl_api_##n##_t), 1);
545 foreach_flowperpkt_plugin_api_msg;
551 #define vl_msg_name_crc_list
552 #include <flowperpkt/flowperpkt_all_api_h.h>
553 #undef vl_msg_name_crc_list
556 setup_message_id_table (flowperpkt_main_t * fm, api_main_t * am)
558 #define _(id,n,crc) \
559 vl_msg_api_add_msg_name_crc (am, #n "_" #crc, id + fm->msg_id_base);
560 foreach_vl_msg_name_crc_flowperpkt;
565 * @brief Set up the API message handling tables
566 * @param vm vlib_main_t * vlib main data structure pointer
567 * @returns 0 to indicate all is well, or a clib_error_t
569 static clib_error_t *
570 flowperpkt_init (vlib_main_t * vm)
572 flowperpkt_main_t *fm = &flowperpkt_main;
573 vlib_thread_main_t *tm = &vlib_thread_main;
574 clib_error_t *error = 0;
578 fm->vnet_main = vnet_get_main ();
580 /* Construct the API name */
581 name = format (0, "flowperpkt_%08x%c", api_version, 0);
583 /* Ask for a correctly-sized block of API message decode slots */
584 fm->msg_id_base = vl_msg_api_get_msg_ids
585 ((char *) name, VL_MSG_FIRST_AVAILABLE);
587 /* Hook up message handlers */
588 error = flowperpkt_plugin_api_hookup (vm);
590 /* Add our API messages to the global name_crc hash table */
591 setup_message_id_table (fm, &api_main);
595 /* Decide how many worker threads we have */
596 num_threads = 1 /* main thread */ + tm->n_threads;
598 /* Allocate per worker thread vectors */
599 vec_validate (fm->ipv4_buffers_per_worker, num_threads - 1);
600 vec_validate (fm->l2_buffers_per_worker, num_threads - 1);
601 vec_validate (fm->ipv4_frames_per_worker, num_threads - 1);
602 vec_validate (fm->l2_frames_per_worker, num_threads - 1);
603 vec_validate (fm->ipv4_next_record_offset_per_worker, num_threads - 1);
604 vec_validate (fm->l2_next_record_offset_per_worker, num_threads - 1);
606 /* Set up time reference pair */
607 fm->vlib_time_0 = vlib_time_now (vm);
608 fm->nanosecond_time_0 = unix_time_now_nsec ();
613 VLIB_INIT_FUNCTION (flowperpkt_init);
616 * fd.io coding-style-patch-verification: ON
619 * eval: (c-set-style "gnu")