2 * flowprobe.c - ipfix probe plugin
4 * Copyright (c) 2016 Cisco and/or its affiliates.
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 <vnet/udp/udp_local.h>
30 #include <flowprobe/flowprobe.h>
32 #include <vlibapi/api.h>
33 #include <vlibmemory/api.h>
35 /* define message IDs */
36 #include <flowprobe/flowprobe.api_enum.h>
37 #include <flowprobe/flowprobe.api_types.h>
39 flowprobe_main_t flowprobe_main;
40 static vlib_node_registration_t flowprobe_timer_node;
41 uword flowprobe_walker_process (vlib_main_t * vm, vlib_node_runtime_t * rt,
44 #define REPLY_MSG_ID_BASE fm->msg_id_base
45 #include <vlibapi/api_helper_macros.h>
47 /* Define the per-interface configurable features */
49 VNET_FEATURE_INIT (flowprobe_input_ip4_unicast, static) = {
50 .arc_name = "ip4-unicast",
51 .node_name = "flowprobe-input-ip4",
52 .runs_before = VNET_FEATURES ("ip4-lookup"),
54 VNET_FEATURE_INIT (flowprobe_input_ip4_multicast, static) = {
55 .arc_name = "ip4-multicast",
56 .node_name = "flowprobe-input-ip4",
57 .runs_before = VNET_FEATURES ("ip4-mfib-forward-lookup"),
59 VNET_FEATURE_INIT (flowprobe_input_ip6_unicast, static) = {
60 .arc_name = "ip6-unicast",
61 .node_name = "flowprobe-input-ip6",
62 .runs_before = VNET_FEATURES ("ip6-lookup"),
64 VNET_FEATURE_INIT (flowprobe_input_ip6_multicast, static) = {
65 .arc_name = "ip6-multicast",
66 .node_name = "flowprobe-input-ip6",
67 .runs_before = VNET_FEATURES ("ip6-mfib-forward-lookup"),
69 VNET_FEATURE_INIT (flowprobe_input_l2, static) = {
70 .arc_name = "device-input",
71 .node_name = "flowprobe-input-l2",
72 .runs_before = VNET_FEATURES ("ethernet-input"),
74 VNET_FEATURE_INIT (flowprobe_output_ip4, static) = {
75 .arc_name = "ip4-output",
76 .node_name = "flowprobe-output-ip4",
77 .runs_before = VNET_FEATURES ("interface-output"),
80 VNET_FEATURE_INIT (flowprobe_output_ip6, static) = {
81 .arc_name = "ip6-output",
82 .node_name = "flowprobe-output-ip6",
83 .runs_before = VNET_FEATURES ("interface-output"),
86 VNET_FEATURE_INIT (flowprobe_output_l2, static) = {
87 .arc_name = "interface-output",
88 .node_name = "flowprobe-output-l2",
89 .runs_before = VNET_FEATURES ("interface-output-arc-end"),
95 vlib_cli_output (handle, (char *) s); \
99 static inline ipfix_field_specifier_t *
100 flowprobe_template_ip4_fields (ipfix_field_specifier_t * f)
102 #define flowprobe_template_ip4_field_count() 4
103 /* sourceIpv4Address, TLV type 8, u32 */
104 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
105 sourceIPv4Address, 4);
107 /* destinationIPv4Address, TLV type 12, u32 */
108 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
109 destinationIPv4Address, 4);
111 /* protocolIdentifier, TLV type 4, u8 */
112 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
113 protocolIdentifier, 1);
115 /* octetDeltaCount, TLV type 1, u64 */
116 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
122 static inline ipfix_field_specifier_t *
123 flowprobe_template_ip6_fields (ipfix_field_specifier_t * f)
125 #define flowprobe_template_ip6_field_count() 4
126 /* sourceIpv6Address, TLV type 27, 16 octets */
127 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
128 sourceIPv6Address, 16);
130 /* destinationIPv6Address, TLV type 28, 16 octets */
131 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
132 destinationIPv6Address, 16);
134 /* protocolIdentifier, TLV type 4, u8 */
135 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
136 protocolIdentifier, 1);
138 /* octetDeltaCount, TLV type 1, u64 */
139 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
145 static inline ipfix_field_specifier_t *
146 flowprobe_template_l2_fields (ipfix_field_specifier_t * f)
148 #define flowprobe_template_l2_field_count() 3
149 /* sourceMacAddress, TLV type 56, u8[6] we hope */
150 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
151 sourceMacAddress, 6);
153 /* destinationMacAddress, TLV type 80, u8[6] we hope */
154 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
155 destinationMacAddress, 6);
157 /* ethernetType, TLV type 256, u16 */
158 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
164 static inline ipfix_field_specifier_t *
165 flowprobe_template_common_fields (ipfix_field_specifier_t * f)
167 #define flowprobe_template_common_field_count() 6
168 /* ingressInterface, TLV type 10, u32 */
169 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
170 ingressInterface, 4);
173 /* egressInterface, TLV type 14, u32 */
174 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
178 /* flowDirection, TLV type 61, u8 */
179 f->e_id_length = ipfix_e_id_length (0 /* enterprise */, flowDirection, 1);
182 /* packetDeltaCount, TLV type 2, u64 */
183 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
184 packetDeltaCount, 8);
187 /* flowStartNanoseconds, TLV type 156, u64 */
188 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
189 flowStartNanoseconds, 8);
192 /* flowEndNanoseconds, TLV type 157, u64 */
193 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
194 flowEndNanoseconds, 8);
200 static inline ipfix_field_specifier_t *
201 flowprobe_template_l4_fields (ipfix_field_specifier_t * f)
203 #define flowprobe_template_l4_field_count() 3
204 /* sourceTransportPort, TLV type 7, u16 */
205 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
206 sourceTransportPort, 2);
208 /* destinationTransportPort, TLV type 11, u16 */
209 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
210 destinationTransportPort, 2);
212 /* tcpControlBits, TLV type 6, u16 */
213 f->e_id_length = ipfix_e_id_length (0 /* enterprise */ ,
221 * @brief Create an IPFIX template packet rewrite string
222 * @param frm flow_report_main_t *
223 * @param fr flow_report_t *
224 * @param collector_address ip4_address_t * the IPFIX collector address
225 * @param src_address ip4_address_t * the source address we should use
226 * @param collector_port u16 the collector port we should use, host byte order
227 * @returns u8 * vector containing the indicated IPFIX template packet
230 flowprobe_template_rewrite_inline (ipfix_exporter_t *exp, flow_report_t *fr,
232 flowprobe_variant_t which)
236 ipfix_message_header_t *h;
237 ipfix_set_header_t *s;
238 ipfix_template_header_t *t;
239 ipfix_field_specifier_t *f;
240 ipfix_field_specifier_t *first_field;
242 ip4_ipfix_template_packet_t *tp;
244 flow_report_stream_t *stream;
245 flowprobe_main_t *fm = &flowprobe_main;
246 flowprobe_record_t flags = fr->opaque.as_uword;
247 bool collect_ip4 = false, collect_ip6 = false;
248 bool collect_l4 = false;
250 stream = &exp->streams[fr->stream_index];
252 if (flags & FLOW_RECORD_L3)
254 collect_ip4 = which == FLOW_VARIANT_L2_IP4 || which == FLOW_VARIANT_IP4;
255 collect_ip6 = which == FLOW_VARIANT_L2_IP6 || which == FLOW_VARIANT_IP6;
256 if (which == FLOW_VARIANT_L2_IP4)
257 flags |= FLOW_RECORD_L2_IP4;
258 if (which == FLOW_VARIANT_L2_IP6)
259 flags |= FLOW_RECORD_L2_IP6;
261 if (flags & FLOW_RECORD_L4)
263 collect_l4 = (which != FLOW_VARIANT_L2);
266 field_count += flowprobe_template_common_field_count ();
267 if (flags & FLOW_RECORD_L2)
268 field_count += flowprobe_template_l2_field_count ();
270 field_count += flowprobe_template_ip4_field_count ();
272 field_count += flowprobe_template_ip6_field_count ();
274 field_count += flowprobe_template_l4_field_count ();
276 /* allocate rewrite space */
278 (rewrite, sizeof (ip4_ipfix_template_packet_t)
279 + field_count * sizeof (ipfix_field_specifier_t) - 1,
280 CLIB_CACHE_LINE_BYTES);
282 tp = (ip4_ipfix_template_packet_t *) rewrite;
283 ip = (ip4_header_t *) & tp->ip4;
284 udp = (udp_header_t *) (ip + 1);
285 h = (ipfix_message_header_t *) (udp + 1);
286 s = (ipfix_set_header_t *) (h + 1);
287 t = (ipfix_template_header_t *) (s + 1);
288 first_field = f = (ipfix_field_specifier_t *) (t + 1);
290 ip->ip_version_and_header_length = 0x45;
292 ip->protocol = IP_PROTOCOL_UDP;
293 ip->src_address.as_u32 = exp->src_address.ip.ip4.as_u32;
294 ip->dst_address.as_u32 = exp->ipfix_collector.ip.ip4.as_u32;
295 udp->src_port = clib_host_to_net_u16 (stream->src_port);
296 udp->dst_port = clib_host_to_net_u16 (collector_port);
297 udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip));
299 /* FIXUP: message header export_time */
300 /* FIXUP: message header sequence_number */
301 h->domain_id = clib_host_to_net_u32 (stream->domain_id);
303 /* Add TLVs to the template */
304 f = flowprobe_template_common_fields (f);
306 if (flags & FLOW_RECORD_L2)
307 f = flowprobe_template_l2_fields (f);
309 f = flowprobe_template_ip4_fields (f);
311 f = flowprobe_template_ip6_fields (f);
313 f = flowprobe_template_l4_fields (f);
315 /* Back to the template packet... */
316 ip = (ip4_header_t *) & tp->ip4;
317 udp = (udp_header_t *) (ip + 1);
319 ASSERT (f - first_field);
320 /* Field count in this template */
321 t->id_count = ipfix_id_count (fr->template_id, f - first_field);
323 fm->template_size[flags] = (u8 *) f - (u8 *) s;
325 /* set length in octets */
327 ipfix_set_id_length (2 /* set_id */ , (u8 *) f - (u8 *) s);
329 /* message length in octets */
330 h->version_length = version_length ((u8 *) f - (u8 *) h);
332 ip->length = clib_host_to_net_u16 ((u8 *) f - (u8 *) ip);
333 ip->checksum = ip4_header_checksum (ip);
339 flowprobe_template_rewrite_ip6 (ipfix_exporter_t *exp, flow_report_t *fr,
341 ipfix_report_element_t *elts, u32 n_elts,
344 return flowprobe_template_rewrite_inline (exp, fr, collector_port,
349 flowprobe_template_rewrite_ip4 (ipfix_exporter_t *exp, flow_report_t *fr,
351 ipfix_report_element_t *elts, u32 n_elts,
354 return flowprobe_template_rewrite_inline (exp, fr, collector_port,
359 flowprobe_template_rewrite_l2 (ipfix_exporter_t *exp, flow_report_t *fr,
361 ipfix_report_element_t *elts, u32 n_elts,
364 return flowprobe_template_rewrite_inline (exp, fr, collector_port,
369 flowprobe_template_rewrite_l2_ip4 (ipfix_exporter_t *exp, flow_report_t *fr,
371 ipfix_report_element_t *elts, u32 n_elts,
374 return flowprobe_template_rewrite_inline (exp, fr, collector_port,
375 FLOW_VARIANT_L2_IP4);
379 flowprobe_template_rewrite_l2_ip6 (ipfix_exporter_t *exp, flow_report_t *fr,
381 ipfix_report_element_t *elts, u32 n_elts,
384 return flowprobe_template_rewrite_inline (exp, fr, collector_port,
385 FLOW_VARIANT_L2_IP6);
389 * @brief Flush accumulated data
390 * @param frm flow_report_main_t *
391 * @param fr flow_report_t *
392 * @param f vlib_frame_t *
395 * This function must simply return the incoming frame, or no template packets
399 flowprobe_data_callback_ip4 (flow_report_main_t *frm, ipfix_exporter_t *exp,
400 flow_report_t *fr, vlib_frame_t *f, u32 *to_next,
403 flowprobe_flush_callback_ip4 ();
408 flowprobe_data_callback_ip6 (flow_report_main_t *frm, ipfix_exporter_t *exp,
409 flow_report_t *fr, vlib_frame_t *f, u32 *to_next,
412 flowprobe_flush_callback_ip6 ();
417 flowprobe_data_callback_l2 (flow_report_main_t *frm, ipfix_exporter_t *exp,
418 flow_report_t *fr, vlib_frame_t *f, u32 *to_next,
421 flowprobe_flush_callback_l2 ();
426 flowprobe_template_add_del (u32 domain_id, u16 src_port,
427 flowprobe_record_t flags,
428 vnet_flow_data_callback_t * flow_data_callback,
429 vnet_flow_rewrite_callback_t * rewrite_callback,
430 bool is_add, u16 * template_id)
432 ipfix_exporter_t *exp = &flow_report_main.exporters[0];
433 vnet_flow_report_add_del_args_t a = {
434 .rewrite_callback = rewrite_callback,
435 .flow_data_callback = flow_data_callback,
437 .domain_id = domain_id,
438 .src_port = src_port,
439 .opaque.as_uword = flags,
441 return vnet_flow_report_add_del (exp, &a, template_id);
445 flowprobe_expired_timer_callback (u32 * expired_timers)
447 vlib_main_t *vm = vlib_get_main ();
448 flowprobe_main_t *fm = &flowprobe_main;
449 u32 my_cpu_number = vm->thread_index;
453 for (i = 0; i < vec_len (expired_timers); i++)
455 poolindex = expired_timers[i] & 0x7FFFFFFF;
456 vec_add1 (fm->expired_passive_per_worker[my_cpu_number], poolindex);
460 static clib_error_t *
461 flowprobe_create_state_tables (u32 active_timer)
463 flowprobe_main_t *fm = &flowprobe_main;
464 vlib_thread_main_t *tm = &vlib_thread_main;
465 vlib_main_t *vm = vlib_get_main ();
466 clib_error_t *error = 0;
470 /* Decide how many worker threads we have */
471 num_threads = 1 /* main thread */ + tm->n_threads;
473 /* Hash table per worker */
474 fm->ht_log2len = FLOWPROBE_LOG2_HASHSIZE;
476 /* Init per worker flow state and timer wheels */
479 vec_validate (fm->timers_per_worker, num_threads - 1);
480 vec_validate (fm->expired_passive_per_worker, num_threads - 1);
481 vec_validate (fm->hash_per_worker, num_threads - 1);
482 vec_validate (fm->pool_per_worker, num_threads - 1);
484 for (i = 0; i < num_threads; i++)
487 pool_alloc (fm->pool_per_worker[i], 1 << fm->ht_log2len);
488 vec_resize (fm->hash_per_worker[i], 1 << fm->ht_log2len);
489 for (j = 0; j < (1 << fm->ht_log2len); j++)
490 fm->hash_per_worker[i][j] = ~0;
491 fm->timers_per_worker[i] =
492 clib_mem_alloc (sizeof (TWT (tw_timer_wheel)));
493 tw_timer_wheel_init_2t_1w_2048sl (fm->timers_per_worker[i],
494 flowprobe_expired_timer_callback,
501 f64 now = vlib_time_now (vm);
502 vec_validate (fm->stateless_entry, num_threads - 1);
503 for (i = 0; i < num_threads; i++)
504 fm->stateless_entry[i].last_exported = now;
505 fm->disabled = false;
507 fm->initialized = true;
511 static clib_error_t *
512 flowprobe_clear_state_if_index (u32 sw_if_index)
514 flowprobe_main_t *fm = &flowprobe_main;
515 clib_error_t *error = 0;
519 if (fm->active_timer > 0)
521 vec_foreach_index (worker_i, fm->pool_per_worker)
523 pool_foreach_index (entry_i, fm->pool_per_worker[worker_i])
525 flowprobe_entry_t *e =
526 pool_elt_at_index (fm->pool_per_worker[worker_i], entry_i);
527 if (e->key.rx_sw_if_index == sw_if_index ||
528 e->key.tx_sw_if_index == sw_if_index)
530 if (fm->passive_timer > 0)
532 tw_timer_stop_2t_1w_2048sl (
533 fm->timers_per_worker[worker_i],
534 e->passive_timer_handle);
536 flowprobe_delete_by_index (worker_i, entry_i);
546 validate_feature_on_interface (flowprobe_main_t * fm, u32 sw_if_index,
549 vec_validate_init_empty (fm->flow_per_interface, sw_if_index, ~0);
550 vec_validate_init_empty (fm->direction_per_interface, sw_if_index, ~0);
552 if (fm->flow_per_interface[sw_if_index] == (u8) ~ 0)
554 else if (fm->flow_per_interface[sw_if_index] != which)
561 * @brief configure / deconfigure the IPFIX flow-per-packet
562 * @param fm flowprobe_main_t * fm
563 * @param sw_if_index u32 the desired interface
564 * @param which u8 the desired datapath
565 * @param direction u8 the desired direction
566 * @param is_add int 1 to enable the feature, 0 to disable it
567 * @returns 0 if successful, non-zero otherwise
571 flowprobe_interface_add_del_feature (flowprobe_main_t *fm, u32 sw_if_index,
572 u8 which, u8 direction, int is_add)
574 vlib_main_t *vm = vlib_get_main ();
577 flowprobe_record_t flags = fm->record;
579 fm->flow_per_interface[sw_if_index] = (is_add) ? which : (u8) ~ 0;
580 fm->direction_per_interface[sw_if_index] = (is_add) ? direction : (u8) ~0;
581 fm->template_per_flow[which] += (is_add) ? 1 : -1;
582 if (is_add && fm->template_per_flow[which] > 1)
583 template_id = fm->template_reports[flags];
585 if ((is_add && fm->template_per_flow[which] == 1) ||
586 (!is_add && fm->template_per_flow[which] == 0))
588 if (which == FLOW_VARIANT_L2)
592 flowprobe_flush_callback_l2 ();
594 if (fm->record & FLOW_RECORD_L2)
596 rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags,
597 flowprobe_data_callback_l2,
598 flowprobe_template_rewrite_l2,
599 is_add, &template_id);
600 fm->template_reports[flags] = (is_add) ? template_id : 0;
602 if (fm->record & FLOW_RECORD_L3 || fm->record & FLOW_RECORD_L4)
604 rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags,
605 flowprobe_data_callback_l2,
606 flowprobe_template_rewrite_l2_ip4,
607 is_add, &template_id);
608 fm->template_reports[flags | FLOW_RECORD_L2_IP4] =
609 (is_add) ? template_id : 0;
611 flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags,
612 flowprobe_data_callback_l2,
613 flowprobe_template_rewrite_l2_ip6,
614 is_add, &template_id);
615 fm->template_reports[flags | FLOW_RECORD_L2_IP6] =
616 (is_add) ? template_id : 0;
618 /* Special case L2 */
619 fm->context[FLOW_VARIANT_L2_IP4].flags =
620 flags | FLOW_RECORD_L2_IP4;
621 fm->context[FLOW_VARIANT_L2_IP6].flags =
622 flags | FLOW_RECORD_L2_IP6;
625 else if (which == FLOW_VARIANT_IP4)
629 flowprobe_flush_callback_ip4 ();
631 rv = flowprobe_template_add_del (
632 1, UDP_DST_PORT_ipfix, flags, flowprobe_data_callback_ip4,
633 flowprobe_template_rewrite_ip4, is_add, &template_id);
634 fm->template_reports[flags] = (is_add) ? template_id : 0;
636 else if (which == FLOW_VARIANT_IP6)
640 flowprobe_flush_callback_ip6 ();
642 rv = flowprobe_template_add_del (
643 1, UDP_DST_PORT_ipfix, flags, flowprobe_data_callback_ip6,
644 flowprobe_template_rewrite_ip6, is_add, &template_id);
645 fm->template_reports[flags] = (is_add) ? template_id : 0;
648 if (rv && rv != VNET_API_ERROR_VALUE_EXIST)
650 clib_warning ("vnet_flow_report_add_del returned %d", rv);
654 if (which != (u8) ~ 0)
656 fm->context[which].flags = fm->record;
659 if (direction == FLOW_DIRECTION_RX || direction == FLOW_DIRECTION_BOTH)
661 if (which == FLOW_VARIANT_IP4)
663 vnet_feature_enable_disable ("ip4-unicast", "flowprobe-input-ip4",
664 sw_if_index, is_add, 0, 0);
665 vnet_feature_enable_disable ("ip4-multicast", "flowprobe-input-ip4",
666 sw_if_index, is_add, 0, 0);
668 else if (which == FLOW_VARIANT_IP6)
670 vnet_feature_enable_disable ("ip6-unicast", "flowprobe-input-ip6",
671 sw_if_index, is_add, 0, 0);
672 vnet_feature_enable_disable ("ip6-multicast", "flowprobe-input-ip6",
673 sw_if_index, is_add, 0, 0);
675 else if (which == FLOW_VARIANT_L2)
676 vnet_feature_enable_disable ("device-input", "flowprobe-input-l2",
677 sw_if_index, is_add, 0, 0);
680 if (direction == FLOW_DIRECTION_TX || direction == FLOW_DIRECTION_BOTH)
682 if (which == FLOW_VARIANT_IP4)
683 vnet_feature_enable_disable ("ip4-output", "flowprobe-output-ip4",
684 sw_if_index, is_add, 0, 0);
685 else if (which == FLOW_VARIANT_IP6)
686 vnet_feature_enable_disable ("ip6-output", "flowprobe-output-ip6",
687 sw_if_index, is_add, 0, 0);
688 else if (which == FLOW_VARIANT_L2)
689 vnet_feature_enable_disable ("interface-output", "flowprobe-output-l2",
690 sw_if_index, is_add, 0, 0);
693 /* Stateful flow collection */
694 if (is_add && !fm->initialized)
696 flowprobe_create_state_tables (fm->active_timer);
697 if (fm->active_timer)
698 vlib_process_signal_event (vm, flowprobe_timer_node.index, 1, 0);
701 if (!is_add && fm->initialized)
703 flowprobe_clear_state_if_index (sw_if_index);
710 * @brief API message handler
711 * @param mp vl_api_flowprobe_tx_interface_add_del_t * mp the api message
713 void vl_api_flowprobe_tx_interface_add_del_t_handler
714 (vl_api_flowprobe_tx_interface_add_del_t * mp)
716 flowprobe_main_t *fm = &flowprobe_main;
717 vl_api_flowprobe_tx_interface_add_del_reply_t *rmp;
718 u32 sw_if_index = ntohl (mp->sw_if_index);
721 VALIDATE_SW_IF_INDEX (mp);
725 clib_warning ("Please specify flowprobe params record first...");
726 rv = VNET_API_ERROR_CANNOT_ENABLE_DISABLE_FEATURE;
730 rv = validate_feature_on_interface (fm, sw_if_index, mp->which);
731 if ((rv == 1 && mp->is_add == 1) || rv == 0)
733 rv = VNET_API_ERROR_CANNOT_ENABLE_DISABLE_FEATURE;
737 rv = flowprobe_interface_add_del_feature (fm, sw_if_index, mp->which,
738 FLOW_DIRECTION_TX, mp->is_add);
741 BAD_SW_IF_INDEX_LABEL;
743 REPLY_MACRO (VL_API_FLOWPROBE_TX_INTERFACE_ADD_DEL_REPLY);
747 vl_api_flowprobe_interface_add_del_t_handler (
748 vl_api_flowprobe_interface_add_del_t *mp)
750 flowprobe_main_t *fm = &flowprobe_main;
751 vl_api_flowprobe_interface_add_del_reply_t *rmp;
758 VALIDATE_SW_IF_INDEX (mp);
760 sw_if_index = ntohl (mp->sw_if_index);
763 if (mp->which == FLOWPROBE_WHICH_IP4)
764 which = FLOW_VARIANT_IP4;
765 else if (mp->which == FLOWPROBE_WHICH_IP6)
766 which = FLOW_VARIANT_IP6;
767 else if (mp->which == FLOWPROBE_WHICH_L2)
768 which = FLOW_VARIANT_L2;
771 clib_warning ("Invalid value of which");
772 rv = VNET_API_ERROR_INVALID_VALUE;
776 if (mp->direction == FLOWPROBE_DIRECTION_RX)
777 direction = FLOW_DIRECTION_RX;
778 else if (mp->direction == FLOWPROBE_DIRECTION_TX)
779 direction = FLOW_DIRECTION_TX;
780 else if (mp->direction == FLOWPROBE_DIRECTION_BOTH)
781 direction = FLOW_DIRECTION_BOTH;
784 clib_warning ("Invalid value of direction");
785 rv = VNET_API_ERROR_INVALID_VALUE;
791 clib_warning ("Please specify flowprobe params record first");
792 rv = VNET_API_ERROR_CANNOT_ENABLE_DISABLE_FEATURE;
796 rv = validate_feature_on_interface (fm, sw_if_index, which);
801 clib_warning ("Variant is already enabled for given interface");
802 rv = VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
808 clib_warning ("Interface has different variant enabled");
809 rv = VNET_API_ERROR_ENTRY_ALREADY_EXISTS;
816 clib_warning ("Interface has no variant enabled");
817 rv = VNET_API_ERROR_NO_SUCH_ENTRY;
822 rv = flowprobe_interface_add_del_feature (fm, sw_if_index, which, direction,
826 BAD_SW_IF_INDEX_LABEL;
828 REPLY_MACRO (VL_API_FLOWPROBE_INTERFACE_ADD_DEL_REPLY);
832 send_flowprobe_interface_details (u32 sw_if_index, u8 which, u8 direction,
833 vl_api_registration_t *reg, u32 context)
835 flowprobe_main_t *fm = &flowprobe_main;
836 vl_api_flowprobe_interface_details_t *rmp = 0;
838 rmp = vl_msg_api_alloc (sizeof (*rmp));
841 clib_memset (rmp, 0, sizeof (*rmp));
843 ntohs (VL_API_FLOWPROBE_INTERFACE_DETAILS + REPLY_MSG_ID_BASE);
844 rmp->context = context;
846 rmp->sw_if_index = htonl (sw_if_index);
848 if (which == FLOW_VARIANT_IP4)
849 rmp->which = FLOWPROBE_WHICH_IP4;
850 else if (which == FLOW_VARIANT_IP6)
851 rmp->which = FLOWPROBE_WHICH_IP6;
852 else if (which == FLOW_VARIANT_L2)
853 rmp->which = FLOWPROBE_WHICH_L2;
857 if (direction == FLOW_DIRECTION_RX)
858 rmp->direction = FLOWPROBE_DIRECTION_RX;
859 else if (direction == FLOW_DIRECTION_TX)
860 rmp->direction = FLOWPROBE_DIRECTION_TX;
861 else if (direction == FLOW_DIRECTION_BOTH)
862 rmp->direction = FLOWPROBE_DIRECTION_BOTH;
866 vl_api_send_msg (reg, (u8 *) rmp);
870 vl_api_flowprobe_interface_dump_t_handler (
871 vl_api_flowprobe_interface_dump_t *mp)
873 flowprobe_main_t *fm = &flowprobe_main;
874 vl_api_registration_t *reg;
877 reg = vl_api_client_index_to_registration (mp->client_index);
881 sw_if_index = ntohl (mp->sw_if_index);
883 if (sw_if_index == ~0)
887 vec_foreach (which, fm->flow_per_interface)
889 if (*which == (u8) ~0)
892 sw_if_index = which - fm->flow_per_interface;
893 send_flowprobe_interface_details (
894 sw_if_index, *which, fm->direction_per_interface[sw_if_index], reg,
898 else if (vec_len (fm->flow_per_interface) > sw_if_index &&
899 fm->flow_per_interface[sw_if_index] != (u8) ~0)
901 send_flowprobe_interface_details (
902 sw_if_index, fm->flow_per_interface[sw_if_index],
903 fm->direction_per_interface[sw_if_index], reg, mp->context);
907 #define vec_neg_search(v,E) \
910 while (_v(i) < vec_len(v) && v[_v(i)] == E) \
914 if (_v(i) == vec_len(v)) \
920 flowprobe_params (flowprobe_main_t * fm, u8 record_l2,
921 u8 record_l3, u8 record_l4,
922 u32 active_timer, u32 passive_timer)
924 flowprobe_record_t flags = 0;
926 if (vec_neg_search (fm->flow_per_interface, (u8) ~ 0) != ~0)
927 return VNET_API_ERROR_UNSUPPORTED;
930 flags |= FLOW_RECORD_L2;
932 flags |= FLOW_RECORD_L3;
934 flags |= FLOW_RECORD_L4;
939 * Timers: ~0 is default, 0 is off
942 (active_timer == (u32) ~ 0 ? FLOWPROBE_TIMER_ACTIVE : active_timer);
944 (passive_timer == (u32) ~ 0 ? FLOWPROBE_TIMER_PASSIVE : passive_timer);
950 vl_api_flowprobe_params_t_handler (vl_api_flowprobe_params_t * mp)
952 flowprobe_main_t *fm = &flowprobe_main;
953 vl_api_flowprobe_params_reply_t *rmp;
956 rv = flowprobe_params
958 mp->record_flags & FLOWPROBE_RECORD_FLAG_L2,
959 mp->record_flags & FLOWPROBE_RECORD_FLAG_L3,
960 mp->record_flags & FLOWPROBE_RECORD_FLAG_L4,
961 clib_net_to_host_u32 (mp->active_timer),
962 clib_net_to_host_u32 (mp->passive_timer));
964 REPLY_MACRO (VL_API_FLOWPROBE_PARAMS_REPLY);
968 vl_api_flowprobe_set_params_t_handler (vl_api_flowprobe_set_params_t *mp)
970 flowprobe_main_t *fm = &flowprobe_main;
971 vl_api_flowprobe_set_params_reply_t *rmp;
972 bool record_l2, record_l3, record_l4;
977 record_l2 = (mp->record_flags & FLOWPROBE_RECORD_FLAG_L2);
978 record_l3 = (mp->record_flags & FLOWPROBE_RECORD_FLAG_L3);
979 record_l4 = (mp->record_flags & FLOWPROBE_RECORD_FLAG_L4);
981 active_timer = clib_net_to_host_u32 (mp->active_timer);
982 passive_timer = clib_net_to_host_u32 (mp->passive_timer);
984 if (passive_timer > 0 && active_timer > passive_timer)
986 clib_warning ("Passive timer must be greater than active timer");
987 rv = VNET_API_ERROR_INVALID_VALUE;
991 rv = flowprobe_params (fm, record_l2, record_l3, record_l4, active_timer,
993 if (rv == VNET_API_ERROR_UNSUPPORTED)
995 "Cannot change params when feature is enabled on some interfaces");
998 REPLY_MACRO (VL_API_FLOWPROBE_SET_PARAMS_REPLY);
1002 vl_api_flowprobe_get_params_t_handler (vl_api_flowprobe_get_params_t *mp)
1004 flowprobe_main_t *fm = &flowprobe_main;
1005 vl_api_flowprobe_get_params_reply_t *rmp;
1006 u8 record_flags = 0;
1009 if (fm->record & FLOW_RECORD_L2)
1010 record_flags |= FLOWPROBE_RECORD_FLAG_L2;
1011 if (fm->record & FLOW_RECORD_L3)
1012 record_flags |= FLOWPROBE_RECORD_FLAG_L3;
1013 if (fm->record & FLOW_RECORD_L4)
1014 record_flags |= FLOWPROBE_RECORD_FLAG_L4;
1017 REPLY_MACRO2 (VL_API_FLOWPROBE_GET_PARAMS_REPLY,
1019 rmp->record_flags = record_flags;
1020 rmp->active_timer = htonl (fm->active_timer);
1021 rmp->passive_timer = htonl (fm->passive_timer);
1027 VLIB_PLUGIN_REGISTER () = {
1028 .version = VPP_BUILD_VER,
1029 .description = "Flow per Packet",
1034 format_flowprobe_direction (u8 *s, va_list *args)
1036 u8 *direction = va_arg (*args, u8 *);
1037 if (*direction == FLOW_DIRECTION_RX)
1038 s = format (s, "rx");
1039 else if (*direction == FLOW_DIRECTION_TX)
1040 s = format (s, "tx");
1041 else if (*direction == FLOW_DIRECTION_BOTH)
1042 s = format (s, "rx tx");
1048 format_flowprobe_entry (u8 * s, va_list * args)
1050 flowprobe_entry_t *e = va_arg (*args, flowprobe_entry_t *);
1051 s = format (s, " %U", format_flowprobe_direction, &e->key.direction);
1052 s = format (s, " %d/%d", e->key.rx_sw_if_index, e->key.tx_sw_if_index);
1054 s = format (s, " %U %U", format_ethernet_address, &e->key.src_mac,
1055 format_ethernet_address, &e->key.dst_mac);
1056 s = format (s, " %U -> %U",
1057 format_ip46_address, &e->key.src_address, IP46_TYPE_ANY,
1058 format_ip46_address, &e->key.dst_address, IP46_TYPE_ANY);
1059 s = format (s, " %d", e->key.protocol);
1060 s = format (s, " %d %d\n", clib_net_to_host_u16 (e->key.src_port),
1061 clib_net_to_host_u16 (e->key.dst_port));
1067 format_flowprobe_feature (u8 * s, va_list * args)
1069 u8 *which = va_arg (*args, u8 *);
1070 if (*which == FLOW_VARIANT_IP4)
1071 s = format (s, "ip4");
1072 else if (*which == FLOW_VARIANT_IP6)
1073 s = format (s, "ip6");
1074 else if (*which == FLOW_VARIANT_L2)
1075 s = format (s, "l2");
1081 format_flowprobe_params (u8 * s, va_list * args)
1083 flowprobe_record_t flags = va_arg (*args, flowprobe_record_t);
1084 u32 active_timer = va_arg (*args, u32);
1085 u32 passive_timer = va_arg (*args, u32);
1087 if (flags & FLOW_RECORD_L2)
1088 s = format (s, " l2");
1089 if (flags & FLOW_RECORD_L3)
1090 s = format (s, " l3");
1091 if (flags & FLOW_RECORD_L4)
1092 s = format (s, " l4");
1094 if (active_timer != (u32) ~ 0)
1095 s = format (s, " active: %d", active_timer);
1097 if (passive_timer != (u32) ~ 0)
1098 s = format (s, " passive: %d", passive_timer);
1103 static clib_error_t *
1104 flowprobe_show_table_fn (vlib_main_t * vm,
1105 unformat_input_t * input, vlib_cli_command_t * cm)
1107 flowprobe_main_t *fm = &flowprobe_main;
1109 flowprobe_entry_t *e;
1111 vlib_cli_output (vm, "Dumping IPFIX table");
1113 for (i = 0; i < vec_len (fm->pool_per_worker); i++)
1116 pool_foreach (e, fm->pool_per_worker[i])
1118 vlib_cli_output (vm, "%U",
1119 format_flowprobe_entry,
1128 static clib_error_t *
1129 flowprobe_show_stats_fn (vlib_main_t * vm,
1130 unformat_input_t * input, vlib_cli_command_t * cm)
1132 flowprobe_main_t *fm = &flowprobe_main;
1135 vlib_cli_output (vm, "IPFIX table statistics");
1136 vlib_cli_output (vm, "Flow entry size: %d\n", sizeof (flowprobe_entry_t));
1137 vlib_cli_output (vm, "Flow pool size per thread: %d\n",
1138 0x1 << FLOWPROBE_LOG2_HASHSIZE);
1140 for (i = 0; i < vec_len (fm->pool_per_worker); i++)
1141 vlib_cli_output (vm, "Pool utilisation thread %d is %d%%\n", i,
1142 (100 * pool_elts (fm->pool_per_worker[i])) /
1143 (0x1 << FLOWPROBE_LOG2_HASHSIZE));
1147 static clib_error_t *
1148 flowprobe_interface_add_del_feature_command_fn (vlib_main_t *vm,
1149 unformat_input_t *input,
1150 vlib_cli_command_t *cmd)
1152 flowprobe_main_t *fm = &flowprobe_main;
1153 u32 sw_if_index = ~0;
1155 u8 which = FLOW_VARIANT_IP4;
1156 flowprobe_direction_t direction = FLOW_DIRECTION_TX;
1159 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1161 if (unformat (input, "disable"))
1163 else if (unformat (input, "%U", unformat_vnet_sw_interface,
1164 fm->vnet_main, &sw_if_index));
1165 else if (unformat (input, "ip4"))
1166 which = FLOW_VARIANT_IP4;
1167 else if (unformat (input, "ip6"))
1168 which = FLOW_VARIANT_IP6;
1169 else if (unformat (input, "l2"))
1170 which = FLOW_VARIANT_L2;
1171 else if (unformat (input, "rx"))
1172 direction = FLOW_DIRECTION_RX;
1173 else if (unformat (input, "tx"))
1174 direction = FLOW_DIRECTION_TX;
1175 else if (unformat (input, "both"))
1176 direction = FLOW_DIRECTION_BOTH;
1181 if (fm->record == 0)
1182 return clib_error_return (0,
1183 "Please specify flowprobe params record first...");
1185 if (sw_if_index == ~0)
1186 return clib_error_return (0, "Please specify an interface...");
1188 rv = validate_feature_on_interface (fm, sw_if_index, which);
1192 return clib_error_return (0,
1193 "Datapath is already enabled for given interface...");
1196 return clib_error_return (0,
1197 "Interface has enable different datapath ...");
1202 return clib_error_return (0, "Interface has no datapath enabled");
1206 rv = flowprobe_interface_add_del_feature (fm, sw_if_index, which, direction,
1213 case VNET_API_ERROR_INVALID_SW_IF_INDEX:
1214 return clib_error_return
1215 (0, "Invalid interface, only works on physical ports");
1218 case VNET_API_ERROR_UNIMPLEMENTED:
1219 return clib_error_return (0, "ip6 not supported");
1223 return clib_error_return (0, "flowprobe_enable_disable returned %d",
1229 static clib_error_t *
1230 flowprobe_show_feature_command_fn (vlib_main_t * vm,
1231 unformat_input_t * input,
1232 vlib_cli_command_t * cmd)
1234 flowprobe_main_t *fm = &flowprobe_main;
1238 vec_foreach (which, fm->flow_per_interface)
1240 if (*which == (u8) ~ 0)
1243 sw_if_index = which - fm->flow_per_interface;
1244 vlib_cli_output (vm, " %U %U %U", format_vnet_sw_if_index_name,
1245 vnet_get_main (), sw_if_index, format_flowprobe_feature,
1246 which, format_flowprobe_direction,
1247 &fm->direction_per_interface[sw_if_index]);
1252 static clib_error_t *
1253 flowprobe_params_command_fn (vlib_main_t * vm,
1254 unformat_input_t * input,
1255 vlib_cli_command_t * cmd)
1257 flowprobe_main_t *fm = &flowprobe_main;
1258 bool record_l2 = false, record_l3 = false, record_l4 = false;
1259 u32 active_timer = ~0;
1260 u32 passive_timer = ~0;
1262 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1264 if (unformat (input, "active %d", &active_timer))
1266 else if (unformat (input, "passive %d", &passive_timer))
1268 else if (unformat (input, "record"))
1269 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
1271 if (unformat (input, "l2"))
1273 else if (unformat (input, "l3"))
1275 else if (unformat (input, "l4"))
1284 if (passive_timer > 0 && active_timer > passive_timer)
1285 return clib_error_return (0,
1286 "Passive timer has to be greater than active one...");
1288 if (flowprobe_params (fm, record_l2, record_l3, record_l4,
1289 active_timer, passive_timer))
1290 return clib_error_return (0,
1291 "Couldn't change flowperpacket params when feature is enabled on some interface ...");
1295 static clib_error_t *
1296 flowprobe_show_params_command_fn (vlib_main_t * vm,
1297 unformat_input_t * input,
1298 vlib_cli_command_t * cmd)
1300 flowprobe_main_t *fm = &flowprobe_main;
1301 flowprobe_record_t flags = fm->record;
1302 u32 active_timer = fm->active_timer;
1303 u32 passive_timer = fm->passive_timer;
1305 vlib_cli_output (vm, "%U", format_flowprobe_params, flags, active_timer,
1311 * '<em>flowprobe feature add-del</em>' commands to enable/disable
1312 * per-packet IPFIX flow record generation on an interface
1316 * To enable per-packet IPFIX flow-record generation on an interface:
1317 * @cliexcmd{flowprobe feature add-del GigabitEthernet2/0/0}
1319 * To disable per-packet IPFIX flow-record generation on an interface:
1320 * @cliexcmd{flowprobe feature add-del GigabitEthernet2/0/0 disable}
1325 VLIB_CLI_COMMAND (flowprobe_enable_disable_command, static) = {
1326 .path = "flowprobe feature add-del",
1327 .short_help = "flowprobe feature add-del <interface-name> [(l2|ip4|ip6)] "
1328 "[(rx|tx|both)] [disable]",
1329 .function = flowprobe_interface_add_del_feature_command_fn,
1331 VLIB_CLI_COMMAND (flowprobe_params_command, static) = {
1332 .path = "flowprobe params",
1333 .short_help = "flowprobe params record [l2] [l3] [l4] [active <timer>] "
1334 "[passive <timer>]",
1335 .function = flowprobe_params_command_fn,
1338 VLIB_CLI_COMMAND (flowprobe_show_feature_command, static) = {
1339 .path = "show flowprobe feature",
1341 "show flowprobe feature",
1342 .function = flowprobe_show_feature_command_fn,
1344 VLIB_CLI_COMMAND (flowprobe_show_params_command, static) = {
1345 .path = "show flowprobe params",
1347 "show flowprobe params",
1348 .function = flowprobe_show_params_command_fn,
1350 VLIB_CLI_COMMAND (flowprobe_show_table_command, static) = {
1351 .path = "show flowprobe table",
1352 .short_help = "show flowprobe table",
1353 .function = flowprobe_show_table_fn,
1355 VLIB_CLI_COMMAND (flowprobe_show_stats_command, static) = {
1356 .path = "show flowprobe statistics",
1357 .short_help = "show flowprobe statistics",
1358 .function = flowprobe_show_stats_fn,
1363 * Main-core process, sending an interrupt to the per worker input
1364 * process that spins the per worker timer wheel.
1367 timer_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f)
1369 uword *event_data = 0;
1370 vlib_main_t **worker_vms = 0, *worker_vm;
1371 flowprobe_main_t *fm = &flowprobe_main;
1373 /* Wait for Godot... */
1374 vlib_process_wait_for_event_or_clock (vm, 1e9);
1375 uword event_type = vlib_process_get_events (vm, &event_data);
1376 if (event_type != 1)
1377 clib_warning ("bogus kickoff event received, %d", event_type);
1378 vec_reset_length (event_data);
1381 if (vlib_get_n_threads () == 0)
1382 vec_add1 (worker_vms, vm);
1385 for (i = 0; i < vlib_get_n_threads (); i++)
1387 worker_vm = vlib_get_main_by_index (i);
1389 vec_add1 (worker_vms, worker_vm);
1392 f64 sleep_duration = 0.1;
1396 /* Send an interrupt to each timer input node */
1397 sleep_duration = 0.1;
1398 for (i = 0; i < vec_len (worker_vms); i++)
1400 worker_vm = worker_vms[i];
1403 vlib_node_set_interrupt_pending (worker_vm,
1404 flowprobe_walker_node.index);
1406 (fm->expired_passive_per_worker[i] > 0) ? 1e-4 : 0.1;
1409 vlib_process_suspend (vm, sleep_duration);
1411 return 0; /* or not */
1415 VLIB_REGISTER_NODE (flowprobe_timer_node,static) = {
1416 .function = timer_process,
1417 .name = "flowprobe-timer-process",
1418 .type = VLIB_NODE_TYPE_PROCESS,
1422 #include <flowprobe/flowprobe.api.c>
1425 * @brief Set up the API message handling tables
1426 * @param vm vlib_main_t * vlib main data structure pointer
1427 * @returns 0 to indicate all is well, or a clib_error_t
1429 static clib_error_t *
1430 flowprobe_init (vlib_main_t * vm)
1432 flowprobe_main_t *fm = &flowprobe_main;
1433 vlib_thread_main_t *tm = &vlib_thread_main;
1434 clib_error_t *error = 0;
1438 fm->vnet_main = vnet_get_main ();
1440 /* Ask for a correctly-sized block of API message decode slots */
1441 fm->msg_id_base = setup_message_id_table ();
1443 /* Set up time reference pair */
1444 fm->vlib_time_0 = vlib_time_now (vm);
1445 fm->nanosecond_time_0 = unix_time_now_nsec ();
1447 clib_memset (fm->template_reports, 0, sizeof (fm->template_reports));
1448 clib_memset (fm->template_size, 0, sizeof (fm->template_size));
1449 clib_memset (fm->template_per_flow, 0, sizeof (fm->template_per_flow));
1451 /* Decide how many worker threads we have */
1452 num_threads = 1 /* main thread */ + tm->n_threads;
1454 /* Allocate per worker thread vectors per flavour */
1455 for (i = 0; i < FLOW_N_VARIANTS; i++)
1457 vec_validate (fm->context[i].buffers_per_worker, num_threads - 1);
1458 vec_validate (fm->context[i].frames_per_worker, num_threads - 1);
1459 vec_validate (fm->context[i].next_record_offset_per_worker,
1463 fm->active_timer = FLOWPROBE_TIMER_ACTIVE;
1464 fm->passive_timer = FLOWPROBE_TIMER_PASSIVE;
1469 VLIB_INIT_FUNCTION (flowprobe_init);
1472 * fd.io coding-style-patch-verification: ON
1475 * eval: (c-set-style "gnu")