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 feature */
62 VNET_FEATURE_INIT (flow_perpacket, static) = {
63 .arc_name = "ip4-output",
64 .node_name = "flowperpkt",
65 .runs_before = VNET_FEATURES ("interface-output"),
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
76 #define REPLY_MACRO(t) \
78 unix_shared_memory_queue_t * q = \
79 vl_api_client_index_to_input_queue (mp->client_index); \
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); \
88 vl_msg_api_send_shmem (q, (u8 *)&rmp); \
91 /* Macro to finish up custom dump fns */
94 vl_print (handle, (char *)s); \
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, \
103 rv = VNET_API_ERROR_INVALID_SW_IF_INDEX; \
104 goto bad_sw_if_index; \
108 #define BAD_SW_IF_INDEX_LABEL \
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
124 flowperpkt_template_rewrite (flow_report_main_t * frm,
126 ip4_address_t * collector_address,
127 ip4_address_t * src_address, u16 collector_port)
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;
137 ip4_ipfix_template_packet_t *tp;
139 flow_report_stream_t *stream;
141 stream = &frm->streams[fr->stream_index];
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
155 /* Currently 4 fields */
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);
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);
172 ip->ip_version_and_header_length = 0x45;
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));
181 /* FIXUP: message header export_time */
182 /* FIXUP: message header sequence_number */
183 h->domain_id = clib_host_to_net_u32 (stream->domain_id);
185 /* Add TLVs to the template */
186 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , egressInterface,
189 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , ipClassOfService,
193 ipfix_e_id_length (0 /* enterprise */ , flowStartNanoseconds,
196 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , dataLinkFrameSize,
199 /* Extend in the obvious way, right here... */
201 /* Back to the template packet... */
202 ip = (ip4_header_t *) & tp->ip4;
203 udp = (udp_header_t *) (ip + 1);
205 ASSERT (f - first_field);
206 /* Field count in this template */
207 t->id_count = ipfix_id_count (fr->template_id, f - first_field);
209 /* set length in octets */
211 ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
213 /* message length in octets */
214 h->version_length = version_length ((u8 *) f - (u8 *) h);
216 ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip);
217 ip->checksum = ip4_header_checksum (ip);
223 * @brief Flush accumulated data
224 * @param frm flow_report_main_t *
225 * @param fr flow_report_t *
226 * @param f vlib_frame_t *
229 * This function must simply return the incoming frame, or no template packets
233 flowperpkt_data_callback (flow_report_main_t * frm,
235 vlib_frame_t * f, u32 * to_next, u32 node_index)
237 flowperpkt_flush_callback ();
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
249 static int flowperpkt_tx_interface_add_del_feature
250 (flowperpkt_main_t * fm, u32 sw_if_index, int is_add)
252 flow_report_main_t *frm = &flow_report_main;
253 vnet_flow_report_add_del_args_t _a, *a = &_a;
256 if (!fm->report_created)
258 memset (a, 0, sizeof (*a));
259 a->rewrite_callback = flowperpkt_template_rewrite;
260 a->flow_data_callback = flowperpkt_data_callback;
262 a->domain_id = 1; /*$$$$ config parameter */
263 a->src_port = 4739; /*$$$$ config parameter */
264 fm->report_created = 1;
266 rv = vnet_flow_report_add_del (frm, a);
269 clib_warning ("vnet_flow_report_add_del returned %d", rv);
274 vnet_feature_enable_disable ("ip4-output", "flowperpkt", sw_if_index,
281 * @brief API message handler
282 * @param mp vl_api_flowperpkt_tx_interface_add_del_t * mp the api message
284 void vl_api_flowperpkt_tx_interface_add_del_t_handler
285 (vl_api_flowperpkt_tx_interface_add_del_t * mp)
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);
292 VALIDATE_SW_IF_INDEX (mp);
294 rv = flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, mp->is_add);
296 BAD_SW_IF_INDEX_LABEL;
298 REPLY_MACRO (VL_API_FLOWPERPKT_TX_INTERFACE_ADD_DEL_REPLY);
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
307 static void *vl_api_flowperpkt_tx_interface_add_del_t_print
308 (vl_api_flowperpkt_tx_interface_add_del_t * mp, void *handle)
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);
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)
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
330 * This routine exists to convince the vlib plugin framework that
331 * we haven't accidentally copied a random .dll into the plugin directory.
333 * Also collects global variable pointers passed from the vpp engine
336 vlib_plugin_register (vlib_main_t * vm, vnet_plugin_handoff_t * h,
339 flowperpkt_main_t *fm = &flowperpkt_main;
340 clib_error_t *error = 0;
343 fm->vnet_main = h->vnet_main;
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)
353 flowperpkt_main_t *fm = &flowperpkt_main;
354 u32 sw_if_index = ~0;
359 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
361 if (unformat (input, "disable"))
363 else if (unformat (input, "%U", unformat_vnet_sw_interface,
364 fm->vnet_main, &sw_if_index))
370 if (sw_if_index == ~0)
371 return clib_error_return (0, "Please specify an interface...");
373 rv = flowperpkt_tx_interface_add_del_feature (fm, sw_if_index, is_add);
379 case VNET_API_ERROR_INVALID_SW_IF_INDEX:
380 return clib_error_return
381 (0, "Invalid interface, only works on physical ports");
384 case VNET_API_ERROR_UNIMPLEMENTED:
385 return clib_error_return (0, "ip6 not supported");
389 return clib_error_return (0, "flowperpkt_enable_disable returned %d",
396 * '<em>flowperpkt feature add-del</em>' commands to enable/disable
397 * per-packet IPFIX flow record generation on an interface
401 * To enable per-packet IPFIX flow-record generation on an interface:
402 * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0}
404 * To disable per-packet IPFIX flow-record generation on an interface:
405 * @cliexcmd{flowperpkt feature add-del GigabitEthernet2/0/0 disable}
410 VLIB_CLI_COMMAND (flowperpkt_enable_disable_command, static) = {
411 .path = "flowperpkt feature add-del",
413 "flowperpkt feature add-del <interface-name> [disable]",
414 .function = flowperpkt_tx_interface_add_del_feature_command_fn,
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
423 static clib_error_t *
424 flowperpkt_plugin_api_hookup (vlib_main_t * vm)
426 flowperpkt_main_t *fm = &flowperpkt_main;
428 vl_msg_api_set_handlers((VL_API_##N + fm->msg_id_base), \
430 vl_api_##n##_t_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;
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
446 static clib_error_t *
447 flowperpkt_init (vlib_main_t * vm)
449 flowperpkt_main_t *fm = &flowperpkt_main;
450 vlib_thread_main_t *tm = &vlib_thread_main;
451 clib_error_t *error = 0;
455 /* Construct the API name */
456 name = format (0, "flowperpkt_%08x%c", api_version, 0);
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);
462 /* Hook up message handlers */
463 error = flowperpkt_plugin_api_hookup (vm);
467 /* Decide how many worker threads we have */
468 num_threads = 1 /* main thread */ + tm->n_eal_threads;
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);
475 /* Set up time reference pair */
476 fm->vlib_time_0 = vlib_time_now (vm);
477 fm->nanosecond_time_0 = unix_time_now_nsec ();
482 VLIB_INIT_FUNCTION (flowperpkt_init);
485 * fd.io coding-style-patch-verification: ON
488 * eval: (c-set-style "gnu")