diff options
Diffstat (limited to 'src/plugins/flowprobe')
-rw-r--r-- | src/plugins/flowprobe/FEATURE.yaml | 9 | ||||
-rw-r--r-- | src/plugins/flowprobe/flowprobe.api | 119 | ||||
-rw-r--r-- | src/plugins/flowprobe/flowprobe.c | 593 | ||||
-rw-r--r-- | src/plugins/flowprobe/flowprobe.h | 19 | ||||
-rw-r--r-- | src/plugins/flowprobe/flowprobe_plugin_doc.md | 13 | ||||
-rw-r--r-- | src/plugins/flowprobe/flowprobe_plugin_doc.rst | 18 | ||||
-rw-r--r-- | src/plugins/flowprobe/flowprobe_test.c | 218 | ||||
-rw-r--r-- | src/plugins/flowprobe/node.c | 327 |
8 files changed, 1086 insertions, 230 deletions
diff --git a/src/plugins/flowprobe/FEATURE.yaml b/src/plugins/flowprobe/FEATURE.yaml index 66382433d03..9c80b12dc9f 100644 --- a/src/plugins/flowprobe/FEATURE.yaml +++ b/src/plugins/flowprobe/FEATURE.yaml @@ -2,12 +2,11 @@ name: IPFIX probe maintainer: Ole Troan <ot@cisco.com> features: - - L2 input feature - - IPv4 / IPv6 input feature - - Recording of L2, L3 and L4 information -description: "IPFIX flow probe. Works in the L2, or IP input feature path." + - L2 input and output feature path + - IPv4 / IPv6 input and output feature path + - Recording of L2, L3, and L4 information +description: "IPFIX flow probe. Works in the L2 or IP feature path both input and output." missing: - - Output path - Export over IPv6 - Export over TCP/SCTP state: production diff --git a/src/plugins/flowprobe/flowprobe.api b/src/plugins/flowprobe/flowprobe.api index 55dd51d3c30..c2090637cc8 100644 --- a/src/plugins/flowprobe/flowprobe.api +++ b/src/plugins/flowprobe/flowprobe.api @@ -5,7 +5,7 @@ used to control the flowprobe plugin */ -option version = "1.0.0"; +option version = "2.1.0"; import "vnet/interface_types.api"; @@ -16,6 +16,13 @@ enum flowprobe_which_flags : u8 FLOWPROBE_WHICH_FLAG_IP6 = 0x4, }; +enum flowprobe_which : u8 +{ + FLOWPROBE_WHICH_IP4 = 0, + FLOWPROBE_WHICH_IP6, + FLOWPROBE_WHICH_L2, +}; + enum flowprobe_record_flags : u8 { FLOWPROBE_RECORD_FLAG_L2 = 0x1, @@ -23,6 +30,13 @@ enum flowprobe_record_flags : u8 FLOWPROBE_RECORD_FLAG_L4 = 0x4, }; +enum flowprobe_direction : u8 +{ + FLOWPROBE_DIRECTION_RX = 0, + FLOWPROBE_DIRECTION_TX, + FLOWPROBE_DIRECTION_BOTH, +}; + /** \brief Enable / disable per-packet IPFIX recording on an interface @param client_index - opaque cookie to identify the sender @param context - sender context, to match reply w/ request @@ -32,6 +46,8 @@ enum flowprobe_record_flags : u8 */ autoreply define flowprobe_tx_interface_add_del { + option replaced_by="flowprobe_interface_add_del"; + /* Client identifier, set from api_main.my_client_index */ u32 client_index; @@ -47,8 +63,59 @@ autoreply define flowprobe_tx_interface_add_del option vat_help = "<intfc> [disable]"; }; +/** \brief Enable or disable IPFIX flow record generation on an interface + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param is_add - add interface if non-zero, else delete + @param which - datapath on which to record flows + @param direction - direction of recorded flows + @param sw_if_index - index of the interface +*/ +autoreply define flowprobe_interface_add_del +{ + option in_progress; + u32 client_index; + u32 context; + bool is_add; + vl_api_flowprobe_which_t which; + vl_api_flowprobe_direction_t direction; + vl_api_interface_index_t sw_if_index; + option vat_help = "(<intfc> | sw_if_index <if-idx>) [(ip4|ip6|l2)] [(rx|tx|both)] [disable]"; +}; + +/** \brief Dump interfaces for which IPFIX flow record generation is enabled + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param sw_if_index - interface index to use as filter (0xffffffff is "all") +*/ +define flowprobe_interface_dump +{ + option in_progress; + u32 client_index; + u32 context; + vl_api_interface_index_t sw_if_index [default=0xffffffff]; + option vat_help = "[<if-idx>]"; +}; + +/** \brief Details about IPFIX flow record generation enabled on interface + @param context - sender context which was passed in the request + @param which - datapath on which to record flows + @param direction - direction of recorded flows + @param sw_if_index - index of the interface +*/ +define flowprobe_interface_details +{ + option in_progress; + u32 context; + vl_api_flowprobe_which_t which; + vl_api_flowprobe_direction_t direction; + vl_api_interface_index_t sw_if_index; +}; + autoreply define flowprobe_params { + option replaced_by="flowprobe_set_params"; + u32 client_index; u32 context; vl_api_flowprobe_record_flags_t record_flags; @@ -56,3 +123,53 @@ autoreply define flowprobe_params u32 passive_timer; /* ~0 is off, 0 is default */ option vat_help = "record <[l2] [l3] [l4]> [active <timer> passive <timer>]"; }; + +/** \brief Set IPFIX flow record generation parameters + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param record_flags - flags indicating what data to record + @param active_timer - time in seconds after which active flow records are + to be exported (0 is "off", 0xffffffff is "use default value") + @param passive_timer - time in seconds after which passive flow records are + to be deleted (0 is "off", 0xffffffff is "use default value") +*/ +autoreply define flowprobe_set_params +{ + option in_progress; + u32 client_index; + u32 context; + vl_api_flowprobe_record_flags_t record_flags; + u32 active_timer [default=0xffffffff]; + u32 passive_timer [default=0xffffffff]; + option vat_help = "record [l2] [l3] [l4] [active <timer>] [passive <timer>]"; +}; + +/** \brief Get IPFIX flow record generation parameters + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request +*/ +define flowprobe_get_params +{ + option in_progress; + u32 client_index; + u32 context; +}; + +/** \brief Reply to get IPFIX flow record generation parameters + @param context - sender context, to match reply w/ request + @param retval - error (0 is "no error") + @param record_flags - flags indicating what data to record + @param active_timer - time in seconds after which active flow records are + to be exported (0 is "off") + @param passive_timer - time in seconds after which passive flow records are + to be deleted (0 is "off") +*/ +define flowprobe_get_params_reply +{ + option in_progress; + u32 context; + i32 retval; + vl_api_flowprobe_record_flags_t record_flags; + u32 active_timer; + u32 passive_timer; +}; diff --git a/src/plugins/flowprobe/flowprobe.c b/src/plugins/flowprobe/flowprobe.c index ffc43bcd440..58a7cfe22f1 100644 --- a/src/plugins/flowprobe/flowprobe.c +++ b/src/plugins/flowprobe/flowprobe.c @@ -45,35 +45,54 @@ uword flowprobe_walker_process (vlib_main_t * vm, vlib_node_runtime_t * rt, #include <vlibapi/api_helper_macros.h> /* Define the per-interface configurable features */ -/* *INDENT-OFF* */ -VNET_FEATURE_INIT (flow_perpacket_ip4, static) = -{ +VNET_FEATURE_INIT (flowprobe_input_ip4_unicast, static) = { + .arc_name = "ip4-unicast", + .node_name = "flowprobe-input-ip4", + .runs_before = VNET_FEATURES ("ip4-lookup"), +}; +VNET_FEATURE_INIT (flowprobe_input_ip4_multicast, static) = { + .arc_name = "ip4-multicast", + .node_name = "flowprobe-input-ip4", + .runs_before = VNET_FEATURES ("ip4-mfib-forward-lookup"), +}; +VNET_FEATURE_INIT (flowprobe_input_ip6_unicast, static) = { + .arc_name = "ip6-unicast", + .node_name = "flowprobe-input-ip6", + .runs_before = VNET_FEATURES ("ip6-lookup"), +}; +VNET_FEATURE_INIT (flowprobe_input_ip6_multicast, static) = { + .arc_name = "ip6-multicast", + .node_name = "flowprobe-input-ip6", + .runs_before = VNET_FEATURES ("ip6-mfib-forward-lookup"), +}; +VNET_FEATURE_INIT (flowprobe_input_l2, static) = { + .arc_name = "device-input", + .node_name = "flowprobe-input-l2", + .runs_before = VNET_FEATURES ("ethernet-input"), +}; +VNET_FEATURE_INIT (flowprobe_output_ip4, static) = { .arc_name = "ip4-output", - .node_name = "flowprobe-ip4", + .node_name = "flowprobe-output-ip4", .runs_before = VNET_FEATURES ("interface-output"), }; -VNET_FEATURE_INIT (flow_perpacket_ip6, static) = -{ +VNET_FEATURE_INIT (flowprobe_output_ip6, static) = { .arc_name = "ip6-output", - .node_name = "flowprobe-ip6", + .node_name = "flowprobe-output-ip6", .runs_before = VNET_FEATURES ("interface-output"), }; -VNET_FEATURE_INIT (flow_perpacket_l2, static) = { +VNET_FEATURE_INIT (flowprobe_output_l2, static) = { .arc_name = "interface-output", - .node_name = "flowprobe-l2", + .node_name = "flowprobe-output-l2", .runs_before = VNET_FEATURES ("interface-output-arc-end"), }; -/* *INDENT-ON* */ -/* Macro to finish up custom dump fns */ -#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) -#define FINISH \ - vec_add1 (s, 0); \ - vl_print (handle, (char *)s); \ - vec_free (s); \ - return handle; +#define FINISH \ + vec_add1 (s, 0); \ + vlib_cli_output (handle, (char *) s); \ + vec_free (s); \ + return handle; static inline ipfix_field_specifier_t * flowprobe_template_ip4_fields (ipfix_field_specifier_t * f) @@ -143,7 +162,7 @@ flowprobe_template_l2_fields (ipfix_field_specifier_t * f) static inline ipfix_field_specifier_t * flowprobe_template_common_fields (ipfix_field_specifier_t * f) { -#define flowprobe_template_common_field_count() 5 +#define flowprobe_template_common_field_count() 6 /* ingressInterface, TLV type 10, u32 */ f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , ingressInterface, 4); @@ -154,6 +173,10 @@ flowprobe_template_common_fields (ipfix_field_specifier_t * f) egressInterface, 4); f++; + /* flowDirection, TLV type 61, u8 */ + f->e_id_length = ipfix_e_id_length (0 /* enterprise */, flowDirection, 1); + f++; + /* packetDeltaCount, TLV type 2, u64 */ f->e_id_length = ipfix_e_id_length (0 /* enterprise */ , packetDeltaCount, 8); @@ -202,10 +225,7 @@ flowprobe_template_l4_fields (ipfix_field_specifier_t * f) * @returns u8 * vector containing the indicated IPFIX template packet */ static inline u8 * -flowprobe_template_rewrite_inline (flow_report_main_t * frm, - flow_report_t * fr, - ip4_address_t * collector_address, - ip4_address_t * src_address, +flowprobe_template_rewrite_inline (ipfix_exporter_t *exp, flow_report_t *fr, u16 collector_port, flowprobe_variant_t which) { @@ -223,8 +243,9 @@ flowprobe_template_rewrite_inline (flow_report_main_t * frm, flowprobe_main_t *fm = &flowprobe_main; flowprobe_record_t flags = fr->opaque.as_uword; bool collect_ip4 = false, collect_ip6 = false; + bool collect_l4 = false; - stream = &frm->streams[fr->stream_index]; + stream = &exp->streams[fr->stream_index]; if (flags & FLOW_RECORD_L3) { @@ -235,6 +256,10 @@ flowprobe_template_rewrite_inline (flow_report_main_t * frm, if (which == FLOW_VARIANT_L2_IP6) flags |= FLOW_RECORD_L2_IP6; } + if (flags & FLOW_RECORD_L4) + { + collect_l4 = (which != FLOW_VARIANT_L2); + } field_count += flowprobe_template_common_field_count (); if (flags & FLOW_RECORD_L2) @@ -243,7 +268,7 @@ flowprobe_template_rewrite_inline (flow_report_main_t * frm, field_count += flowprobe_template_ip4_field_count (); if (collect_ip6) field_count += flowprobe_template_ip6_field_count (); - if (flags & FLOW_RECORD_L4) + if (collect_l4) field_count += flowprobe_template_l4_field_count (); /* allocate rewrite space */ @@ -263,8 +288,8 @@ flowprobe_template_rewrite_inline (flow_report_main_t * frm, ip->ip_version_and_header_length = 0x45; ip->ttl = 254; ip->protocol = IP_PROTOCOL_UDP; - ip->src_address.as_u32 = src_address->as_u32; - ip->dst_address.as_u32 = collector_address->as_u32; + ip->src_address.as_u32 = exp->src_address.ip.ip4.as_u32; + ip->dst_address.as_u32 = exp->ipfix_collector.ip.ip4.as_u32; udp->src_port = clib_host_to_net_u16 (stream->src_port); udp->dst_port = clib_host_to_net_u16 (collector_port); udp->length = clib_host_to_net_u16 (vec_len (rewrite) - sizeof (*ip)); @@ -282,7 +307,7 @@ flowprobe_template_rewrite_inline (flow_report_main_t * frm, f = flowprobe_template_ip4_fields (f); if (collect_ip6) f = flowprobe_template_ip6_fields (f); - if (flags & FLOW_RECORD_L4) + if (collect_l4) f = flowprobe_template_l4_fields (f); /* Back to the template packet... */ @@ -309,73 +334,53 @@ flowprobe_template_rewrite_inline (flow_report_main_t * frm, } static u8 * -flowprobe_template_rewrite_ip6 (flow_report_main_t * frm, - flow_report_t * fr, - ip4_address_t * collector_address, - ip4_address_t * src_address, +flowprobe_template_rewrite_ip6 (ipfix_exporter_t *exp, flow_report_t *fr, u16 collector_port, - ipfix_report_element_t * elts, - u32 n_elts, u32 * stream_index) + ipfix_report_element_t *elts, u32 n_elts, + u32 *stream_index) { - return flowprobe_template_rewrite_inline - (frm, fr, collector_address, src_address, collector_port, - FLOW_VARIANT_IP6); + return flowprobe_template_rewrite_inline (exp, fr, collector_port, + FLOW_VARIANT_IP6); } static u8 * -flowprobe_template_rewrite_ip4 (flow_report_main_t * frm, - flow_report_t * fr, - ip4_address_t * collector_address, - ip4_address_t * src_address, +flowprobe_template_rewrite_ip4 (ipfix_exporter_t *exp, flow_report_t *fr, u16 collector_port, - ipfix_report_element_t * elts, - u32 n_elts, u32 * stream_index) + ipfix_report_element_t *elts, u32 n_elts, + u32 *stream_index) { - return flowprobe_template_rewrite_inline - (frm, fr, collector_address, src_address, collector_port, - FLOW_VARIANT_IP4); + return flowprobe_template_rewrite_inline (exp, fr, collector_port, + FLOW_VARIANT_IP4); } static u8 * -flowprobe_template_rewrite_l2 (flow_report_main_t * frm, - flow_report_t * fr, - ip4_address_t * collector_address, - ip4_address_t * src_address, +flowprobe_template_rewrite_l2 (ipfix_exporter_t *exp, flow_report_t *fr, u16 collector_port, - ipfix_report_element_t * elts, - u32 n_elts, u32 * stream_index) + ipfix_report_element_t *elts, u32 n_elts, + u32 *stream_index) { - return flowprobe_template_rewrite_inline - (frm, fr, collector_address, src_address, collector_port, - FLOW_VARIANT_L2); + return flowprobe_template_rewrite_inline (exp, fr, collector_port, + FLOW_VARIANT_L2); } static u8 * -flowprobe_template_rewrite_l2_ip4 (flow_report_main_t * frm, - flow_report_t * fr, - ip4_address_t * collector_address, - ip4_address_t * src_address, +flowprobe_template_rewrite_l2_ip4 (ipfix_exporter_t *exp, flow_report_t *fr, u16 collector_port, - ipfix_report_element_t * elts, - u32 n_elts, u32 * stream_index) + ipfix_report_element_t *elts, u32 n_elts, + u32 *stream_index) { - return flowprobe_template_rewrite_inline - (frm, fr, collector_address, src_address, collector_port, - FLOW_VARIANT_L2_IP4); + return flowprobe_template_rewrite_inline (exp, fr, collector_port, + FLOW_VARIANT_L2_IP4); } static u8 * -flowprobe_template_rewrite_l2_ip6 (flow_report_main_t * frm, - flow_report_t * fr, - ip4_address_t * collector_address, - ip4_address_t * src_address, +flowprobe_template_rewrite_l2_ip6 (ipfix_exporter_t *exp, flow_report_t *fr, u16 collector_port, - ipfix_report_element_t * elts, - u32 n_elts, u32 * stream_index) + ipfix_report_element_t *elts, u32 n_elts, + u32 *stream_index) { - return flowprobe_template_rewrite_inline - (frm, fr, collector_address, src_address, collector_port, - FLOW_VARIANT_L2_IP6); + return flowprobe_template_rewrite_inline (exp, fr, collector_port, + FLOW_VARIANT_L2_IP6); } /** @@ -389,27 +394,27 @@ flowprobe_template_rewrite_l2_ip6 (flow_report_main_t * frm, * will be sent. */ vlib_frame_t * -flowprobe_data_callback_ip4 (flow_report_main_t * frm, - flow_report_t * fr, - vlib_frame_t * f, u32 * to_next, u32 node_index) +flowprobe_data_callback_ip4 (flow_report_main_t *frm, ipfix_exporter_t *exp, + flow_report_t *fr, vlib_frame_t *f, u32 *to_next, + u32 node_index) { flowprobe_flush_callback_ip4 (); return f; } vlib_frame_t * -flowprobe_data_callback_ip6 (flow_report_main_t * frm, - flow_report_t * fr, - vlib_frame_t * f, u32 * to_next, u32 node_index) +flowprobe_data_callback_ip6 (flow_report_main_t *frm, ipfix_exporter_t *exp, + flow_report_t *fr, vlib_frame_t *f, u32 *to_next, + u32 node_index) { flowprobe_flush_callback_ip6 (); return f; } vlib_frame_t * -flowprobe_data_callback_l2 (flow_report_main_t * frm, - flow_report_t * fr, - vlib_frame_t * f, u32 * to_next, u32 node_index) +flowprobe_data_callback_l2 (flow_report_main_t *frm, ipfix_exporter_t *exp, + flow_report_t *fr, vlib_frame_t *f, u32 *to_next, + u32 node_index) { flowprobe_flush_callback_l2 (); return f; @@ -422,7 +427,7 @@ flowprobe_template_add_del (u32 domain_id, u16 src_port, vnet_flow_rewrite_callback_t * rewrite_callback, bool is_add, u16 * template_id) { - flow_report_main_t *frm = &flow_report_main; + ipfix_exporter_t *exp = &flow_report_main.exporters[0]; vnet_flow_report_add_del_args_t a = { .rewrite_callback = rewrite_callback, .flow_data_callback = flow_data_callback, @@ -431,7 +436,7 @@ flowprobe_template_add_del (u32 domain_id, u16 src_port, .src_port = src_port, .opaque.as_uword = flags, }; - return vnet_flow_report_add_del (frm, &a, template_id); + return vnet_flow_report_add_del (exp, &a, template_id); } static void @@ -501,11 +506,49 @@ flowprobe_create_state_tables (u32 active_timer) return error; } +static clib_error_t * +flowprobe_clear_state_if_index (u32 sw_if_index) +{ + flowprobe_main_t *fm = &flowprobe_main; + clib_error_t *error = 0; + u32 worker_i; + u32 entry_i; + + if (fm->active_timer > 0) + { + vec_foreach_index (worker_i, fm->pool_per_worker) + { + pool_foreach_index (entry_i, fm->pool_per_worker[worker_i]) + { + flowprobe_entry_t *e = + pool_elt_at_index (fm->pool_per_worker[worker_i], entry_i); + if (e->key.rx_sw_if_index == sw_if_index || + e->key.tx_sw_if_index == sw_if_index) + { + e->packetcount = 0; + e->octetcount = 0; + e->prot.tcp.flags = 0; + if (fm->passive_timer > 0) + { + tw_timer_stop_2t_1w_2048sl ( + fm->timers_per_worker[worker_i], + e->passive_timer_handle); + } + flowprobe_delete_by_index (worker_i, entry_i); + } + } + } + } + + return error; +} + static int validate_feature_on_interface (flowprobe_main_t * fm, u32 sw_if_index, u8 which) { vec_validate_init_empty (fm->flow_per_interface, sw_if_index, ~0); + vec_validate_init_empty (fm->direction_per_interface, sw_if_index, ~0); if (fm->flow_per_interface[sw_if_index] == (u8) ~ 0) return -1; @@ -519,13 +562,15 @@ validate_feature_on_interface (flowprobe_main_t * fm, u32 sw_if_index, * @brief configure / deconfigure the IPFIX flow-per-packet * @param fm flowprobe_main_t * fm * @param sw_if_index u32 the desired interface + * @param which u8 the desired datapath + * @param direction u8 the desired direction * @param is_add int 1 to enable the feature, 0 to disable it * @returns 0 if successful, non-zero otherwise */ static int -flowprobe_tx_interface_add_del_feature (flowprobe_main_t * fm, - u32 sw_if_index, u8 which, int is_add) +flowprobe_interface_add_del_feature (flowprobe_main_t *fm, u32 sw_if_index, + u8 which, u8 direction, int is_add) { vlib_main_t *vm = vlib_get_main (); int rv = 0; @@ -533,6 +578,7 @@ flowprobe_tx_interface_add_del_feature (flowprobe_main_t * fm, flowprobe_record_t flags = fm->record; fm->flow_per_interface[sw_if_index] = (is_add) ? which : (u8) ~ 0; + fm->direction_per_interface[sw_if_index] = (is_add) ? direction : (u8) ~0; fm->template_per_flow[which] += (is_add) ? 1 : -1; if (is_add && fm->template_per_flow[which] > 1) template_id = fm->template_reports[flags]; @@ -542,12 +588,17 @@ flowprobe_tx_interface_add_del_feature (flowprobe_main_t * fm, { if (which == FLOW_VARIANT_L2) { + if (!is_add) + { + flowprobe_flush_callback_l2 (); + } if (fm->record & FLOW_RECORD_L2) { rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags, flowprobe_data_callback_l2, flowprobe_template_rewrite_l2, is_add, &template_id); + fm->template_reports[flags] = (is_add) ? template_id : 0; } if (fm->record & FLOW_RECORD_L3 || fm->record & FLOW_RECORD_L4) { @@ -570,20 +621,30 @@ flowprobe_tx_interface_add_del_feature (flowprobe_main_t * fm, flags | FLOW_RECORD_L2_IP4; fm->context[FLOW_VARIANT_L2_IP6].flags = flags | FLOW_RECORD_L2_IP6; - - fm->template_reports[flags] = template_id; } } else if (which == FLOW_VARIANT_IP4) - rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags, - flowprobe_data_callback_ip4, - flowprobe_template_rewrite_ip4, - is_add, &template_id); + { + if (!is_add) + { + flowprobe_flush_callback_ip4 (); + } + rv = flowprobe_template_add_del ( + 1, UDP_DST_PORT_ipfix, flags, flowprobe_data_callback_ip4, + flowprobe_template_rewrite_ip4, is_add, &template_id); + fm->template_reports[flags] = (is_add) ? template_id : 0; + } else if (which == FLOW_VARIANT_IP6) - rv = flowprobe_template_add_del (1, UDP_DST_PORT_ipfix, flags, - flowprobe_data_callback_ip6, - flowprobe_template_rewrite_ip6, - is_add, &template_id); + { + if (!is_add) + { + flowprobe_flush_callback_ip6 (); + } + rv = flowprobe_template_add_del ( + 1, UDP_DST_PORT_ipfix, flags, flowprobe_data_callback_ip6, + flowprobe_template_rewrite_ip6, is_add, &template_id); + fm->template_reports[flags] = (is_add) ? template_id : 0; + } } if (rv && rv != VNET_API_ERROR_VALUE_EXIST) { @@ -594,18 +655,41 @@ flowprobe_tx_interface_add_del_feature (flowprobe_main_t * fm, if (which != (u8) ~ 0) { fm->context[which].flags = fm->record; - fm->template_reports[flags] = (is_add) ? template_id : 0; } - if (which == FLOW_VARIANT_IP4) - vnet_feature_enable_disable ("ip4-output", "flowprobe-ip4", - sw_if_index, is_add, 0, 0); - else if (which == FLOW_VARIANT_IP6) - vnet_feature_enable_disable ("ip6-output", "flowprobe-ip6", - sw_if_index, is_add, 0, 0); - else if (which == FLOW_VARIANT_L2) - vnet_feature_enable_disable ("interface-output", "flowprobe-l2", - sw_if_index, is_add, 0, 0); + if (direction == FLOW_DIRECTION_RX || direction == FLOW_DIRECTION_BOTH) + { + if (which == FLOW_VARIANT_IP4) + { + vnet_feature_enable_disable ("ip4-unicast", "flowprobe-input-ip4", + sw_if_index, is_add, 0, 0); + vnet_feature_enable_disable ("ip4-multicast", "flowprobe-input-ip4", + sw_if_index, is_add, 0, 0); + } + else if (which == FLOW_VARIANT_IP6) + { + vnet_feature_enable_disable ("ip6-unicast", "flowprobe-input-ip6", + sw_if_index, is_add, 0, 0); + vnet_feature_enable_disable ("ip6-multicast", "flowprobe-input-ip6", + sw_if_index, is_add, 0, 0); + } + else if (which == FLOW_VARIANT_L2) + vnet_feature_enable_disable ("device-input", "flowprobe-input-l2", + sw_if_index, is_add, 0, 0); + } + + if (direction == FLOW_DIRECTION_TX || direction == FLOW_DIRECTION_BOTH) + { + if (which == FLOW_VARIANT_IP4) + vnet_feature_enable_disable ("ip4-output", "flowprobe-output-ip4", + sw_if_index, is_add, 0, 0); + else if (which == FLOW_VARIANT_IP6) + vnet_feature_enable_disable ("ip6-output", "flowprobe-output-ip6", + sw_if_index, is_add, 0, 0); + else if (which == FLOW_VARIANT_L2) + vnet_feature_enable_disable ("interface-output", "flowprobe-output-l2", + sw_if_index, is_add, 0, 0); + } /* Stateful flow collection */ if (is_add && !fm->initialized) @@ -615,6 +699,11 @@ flowprobe_tx_interface_add_del_feature (flowprobe_main_t * fm, vlib_process_signal_event (vm, flowprobe_timer_node.index, 1, 0); } + if (!is_add && fm->initialized) + { + flowprobe_clear_state_if_index (sw_if_index); + } + return 0; } @@ -646,8 +735,8 @@ void vl_api_flowprobe_tx_interface_add_del_t_handler goto out; } - rv = flowprobe_tx_interface_add_del_feature - (fm, sw_if_index, mp->which, mp->is_add); + rv = flowprobe_interface_add_del_feature (fm, sw_if_index, mp->which, + FLOW_DIRECTION_TX, mp->is_add); out: BAD_SW_IF_INDEX_LABEL; @@ -655,6 +744,167 @@ out: REPLY_MACRO (VL_API_FLOWPROBE_TX_INTERFACE_ADD_DEL_REPLY); } +void +vl_api_flowprobe_interface_add_del_t_handler ( + vl_api_flowprobe_interface_add_del_t *mp) +{ + flowprobe_main_t *fm = &flowprobe_main; + vl_api_flowprobe_interface_add_del_reply_t *rmp; + u32 sw_if_index; + u8 which; + u8 direction; + bool is_add; + int rv = 0; + + VALIDATE_SW_IF_INDEX (mp); + + sw_if_index = ntohl (mp->sw_if_index); + is_add = mp->is_add; + + if (mp->which == FLOWPROBE_WHICH_IP4) + which = FLOW_VARIANT_IP4; + else if (mp->which == FLOWPROBE_WHICH_IP6) + which = FLOW_VARIANT_IP6; + else if (mp->which == FLOWPROBE_WHICH_L2) + which = FLOW_VARIANT_L2; + else + { + clib_warning ("Invalid value of which"); + rv = VNET_API_ERROR_INVALID_VALUE; + goto out; + } + + if (mp->direction == FLOWPROBE_DIRECTION_RX) + direction = FLOW_DIRECTION_RX; + else if (mp->direction == FLOWPROBE_DIRECTION_TX) + direction = FLOW_DIRECTION_TX; + else if (mp->direction == FLOWPROBE_DIRECTION_BOTH) + direction = FLOW_DIRECTION_BOTH; + else + { + clib_warning ("Invalid value of direction"); + rv = VNET_API_ERROR_INVALID_VALUE; + goto out; + } + + if (fm->record == 0) + { + clib_warning ("Please specify flowprobe params record first"); + rv = VNET_API_ERROR_CANNOT_ENABLE_DISABLE_FEATURE; + goto out; + } + + rv = validate_feature_on_interface (fm, sw_if_index, which); + if (rv == 1) + { + if (is_add) + { + clib_warning ("Variant is already enabled for given interface"); + rv = VNET_API_ERROR_ENTRY_ALREADY_EXISTS; + goto out; + } + } + else if (rv == 0) + { + clib_warning ("Interface has different variant enabled"); + rv = VNET_API_ERROR_ENTRY_ALREADY_EXISTS; + goto out; + } + else if (rv == -1) + { + if (!is_add) + { + clib_warning ("Interface has no variant enabled"); + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto out; + } + } + + rv = flowprobe_interface_add_del_feature (fm, sw_if_index, which, direction, + is_add); + +out: + BAD_SW_IF_INDEX_LABEL; + + REPLY_MACRO (VL_API_FLOWPROBE_INTERFACE_ADD_DEL_REPLY); +} + +static void +send_flowprobe_interface_details (u32 sw_if_index, u8 which, u8 direction, + vl_api_registration_t *reg, u32 context) +{ + flowprobe_main_t *fm = &flowprobe_main; + vl_api_flowprobe_interface_details_t *rmp = 0; + + rmp = vl_msg_api_alloc (sizeof (*rmp)); + if (!rmp) + return; + clib_memset (rmp, 0, sizeof (*rmp)); + rmp->_vl_msg_id = + ntohs (VL_API_FLOWPROBE_INTERFACE_DETAILS + REPLY_MSG_ID_BASE); + rmp->context = context; + + rmp->sw_if_index = htonl (sw_if_index); + + if (which == FLOW_VARIANT_IP4) + rmp->which = FLOWPROBE_WHICH_IP4; + else if (which == FLOW_VARIANT_IP6) + rmp->which = FLOWPROBE_WHICH_IP6; + else if (which == FLOW_VARIANT_L2) + rmp->which = FLOWPROBE_WHICH_L2; + else + ASSERT (0); + + if (direction == FLOW_DIRECTION_RX) + rmp->direction = FLOWPROBE_DIRECTION_RX; + else if (direction == FLOW_DIRECTION_TX) + rmp->direction = FLOWPROBE_DIRECTION_TX; + else if (direction == FLOW_DIRECTION_BOTH) + rmp->direction = FLOWPROBE_DIRECTION_BOTH; + else + ASSERT (0); + + vl_api_send_msg (reg, (u8 *) rmp); +} + +static void +vl_api_flowprobe_interface_dump_t_handler ( + vl_api_flowprobe_interface_dump_t *mp) +{ + flowprobe_main_t *fm = &flowprobe_main; + vl_api_registration_t *reg; + u32 sw_if_index; + + reg = vl_api_client_index_to_registration (mp->client_index); + if (!reg) + return; + + sw_if_index = ntohl (mp->sw_if_index); + + if (sw_if_index == ~0) + { + u8 *which; + + vec_foreach (which, fm->flow_per_interface) + { + if (*which == (u8) ~0) + continue; + + sw_if_index = which - fm->flow_per_interface; + send_flowprobe_interface_details ( + sw_if_index, *which, fm->direction_per_interface[sw_if_index], reg, + mp->context); + } + } + else if (vec_len (fm->flow_per_interface) > sw_if_index && + fm->flow_per_interface[sw_if_index] != (u8) ~0) + { + send_flowprobe_interface_details ( + sw_if_index, fm->flow_per_interface[sw_if_index], + fm->direction_per_interface[sw_if_index], reg, mp->context); + } +} + #define vec_neg_search(v,E) \ ({ \ word _v(i) = 0; \ @@ -675,7 +925,7 @@ flowprobe_params (flowprobe_main_t * fm, u8 record_l2, flowprobe_record_t flags = 0; if (vec_neg_search (fm->flow_per_interface, (u8) ~ 0) != ~0) - return ~0; + return VNET_API_ERROR_UNSUPPORTED; if (record_l2) flags |= FLOW_RECORD_L2; @@ -715,17 +965,89 @@ vl_api_flowprobe_params_t_handler (vl_api_flowprobe_params_t * mp) REPLY_MACRO (VL_API_FLOWPROBE_PARAMS_REPLY); } -/* *INDENT-OFF* */ +void +vl_api_flowprobe_set_params_t_handler (vl_api_flowprobe_set_params_t *mp) +{ + flowprobe_main_t *fm = &flowprobe_main; + vl_api_flowprobe_set_params_reply_t *rmp; + bool record_l2, record_l3, record_l4; + u32 active_timer; + u32 passive_timer; + int rv = 0; + + record_l2 = (mp->record_flags & FLOWPROBE_RECORD_FLAG_L2); + record_l3 = (mp->record_flags & FLOWPROBE_RECORD_FLAG_L3); + record_l4 = (mp->record_flags & FLOWPROBE_RECORD_FLAG_L4); + + active_timer = clib_net_to_host_u32 (mp->active_timer); + passive_timer = clib_net_to_host_u32 (mp->passive_timer); + + if (passive_timer > 0 && active_timer > passive_timer) + { + clib_warning ("Passive timer must be greater than active timer"); + rv = VNET_API_ERROR_INVALID_VALUE; + goto out; + } + + rv = flowprobe_params (fm, record_l2, record_l3, record_l4, active_timer, + passive_timer); + if (rv == VNET_API_ERROR_UNSUPPORTED) + clib_warning ( + "Cannot change params when feature is enabled on some interfaces"); + +out: + REPLY_MACRO (VL_API_FLOWPROBE_SET_PARAMS_REPLY); +} + +void +vl_api_flowprobe_get_params_t_handler (vl_api_flowprobe_get_params_t *mp) +{ + flowprobe_main_t *fm = &flowprobe_main; + vl_api_flowprobe_get_params_reply_t *rmp; + u8 record_flags = 0; + int rv = 0; + + if (fm->record & FLOW_RECORD_L2) + record_flags |= FLOWPROBE_RECORD_FLAG_L2; + if (fm->record & FLOW_RECORD_L3) + record_flags |= FLOWPROBE_RECORD_FLAG_L3; + if (fm->record & FLOW_RECORD_L4) + record_flags |= FLOWPROBE_RECORD_FLAG_L4; + + // clang-format off + REPLY_MACRO2 (VL_API_FLOWPROBE_GET_PARAMS_REPLY, + ({ + rmp->record_flags = record_flags; + rmp->active_timer = htonl (fm->active_timer); + rmp->passive_timer = htonl (fm->passive_timer); + })); + // clang-format on +} + VLIB_PLUGIN_REGISTER () = { .version = VPP_BUILD_VER, .description = "Flow per Packet", }; -/* *INDENT-ON* */ + +u8 * +format_flowprobe_direction (u8 *s, va_list *args) +{ + u8 *direction = va_arg (*args, u8 *); + if (*direction == FLOW_DIRECTION_RX) + s = format (s, "rx"); + else if (*direction == FLOW_DIRECTION_TX) + s = format (s, "tx"); + else if (*direction == FLOW_DIRECTION_BOTH) + s = format (s, "rx tx"); + + return s; +} u8 * format_flowprobe_entry (u8 * s, va_list * args) { flowprobe_entry_t *e = va_arg (*args, flowprobe_entry_t *); + s = format (s, " %U", format_flowprobe_direction, &e->key.direction); s = format (s, " %d/%d", e->key.rx_sw_if_index, e->key.tx_sw_if_index); s = format (s, " %U %U", format_ethernet_address, &e->key.src_mac, @@ -789,14 +1111,12 @@ flowprobe_show_table_fn (vlib_main_t * vm, for (i = 0; i < vec_len (fm->pool_per_worker); i++) { - /* *INDENT-OFF* */ pool_foreach (e, fm->pool_per_worker[i]) { vlib_cli_output (vm, "%U", format_flowprobe_entry, e); } - /* *INDENT-ON* */ } return 0; @@ -822,14 +1142,15 @@ flowprobe_show_stats_fn (vlib_main_t * vm, } static clib_error_t * -flowprobe_tx_interface_add_del_feature_command_fn (vlib_main_t * vm, - unformat_input_t * input, - vlib_cli_command_t * cmd) +flowprobe_interface_add_del_feature_command_fn (vlib_main_t *vm, + unformat_input_t *input, + vlib_cli_command_t *cmd) { flowprobe_main_t *fm = &flowprobe_main; u32 sw_if_index = ~0; int is_add = 1; u8 which = FLOW_VARIANT_IP4; + flowprobe_direction_t direction = FLOW_DIRECTION_TX; int rv; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) @@ -844,6 +1165,12 @@ flowprobe_tx_interface_add_del_feature_command_fn (vlib_main_t * vm, which = FLOW_VARIANT_IP6; else if (unformat (input, "l2")) which = FLOW_VARIANT_L2; + else if (unformat (input, "rx")) + direction = FLOW_DIRECTION_RX; + else if (unformat (input, "tx")) + direction = FLOW_DIRECTION_TX; + else if (unformat (input, "both")) + direction = FLOW_DIRECTION_BOTH; else break; } @@ -865,9 +1192,16 @@ flowprobe_tx_interface_add_del_feature_command_fn (vlib_main_t * vm, else if (rv == 0) return clib_error_return (0, "Interface has enable different datapath ..."); + else if (rv == -1) + { + if (!is_add) + { + return clib_error_return (0, "Interface has no datapath enabled"); + } + } - rv = - flowprobe_tx_interface_add_del_feature (fm, sw_if_index, which, is_add); + rv = flowprobe_interface_add_del_feature (fm, sw_if_index, which, direction, + is_add); switch (rv) { case 0: @@ -904,9 +1238,10 @@ flowprobe_show_feature_command_fn (vlib_main_t * vm, continue; sw_if_index = which - fm->flow_per_interface; - vlib_cli_output (vm, " %U %U", format_vnet_sw_if_index_name, + vlib_cli_output (vm, " %U %U %U", format_vnet_sw_if_index_name, vnet_get_main (), sw_if_index, format_flowprobe_feature, - which); + which, format_flowprobe_direction, + &fm->direction_per_interface[sw_if_index]); } return 0; } @@ -983,18 +1318,17 @@ flowprobe_show_params_command_fn (vlib_main_t * vm, * @cliexend * @endparblock ?*/ -/* *INDENT-OFF* */ VLIB_CLI_COMMAND (flowprobe_enable_disable_command, static) = { - .path = "flowprobe feature add-del", - .short_help = - "flowprobe feature add-del <interface-name> <l2|ip4|ip6> disable", - .function = flowprobe_tx_interface_add_del_feature_command_fn, + .path = "flowprobe feature add-del", + .short_help = "flowprobe feature add-del <interface-name> [(l2|ip4|ip6)] " + "[(rx|tx|both)] [disable]", + .function = flowprobe_interface_add_del_feature_command_fn, }; VLIB_CLI_COMMAND (flowprobe_params_command, static) = { - .path = "flowprobe params", - .short_help = - "flowprobe params record <[l2] [l3] [l4]> [active <timer> passive <timer>]", - .function = flowprobe_params_command_fn, + .path = "flowprobe params", + .short_help = "flowprobe params record [l2] [l3] [l4] [active <timer>] " + "[passive <timer>]", + .function = flowprobe_params_command_fn, }; VLIB_CLI_COMMAND (flowprobe_show_feature_command, static) = { @@ -1019,7 +1353,6 @@ VLIB_CLI_COMMAND (flowprobe_show_stats_command, static) = { .short_help = "show flowprobe statistics", .function = flowprobe_show_stats_fn, }; -/* *INDENT-ON* */ /* * Main-core process, sending an interrupt to the per worker input @@ -1073,13 +1406,11 @@ timer_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) return 0; /* or not */ } -/* *INDENT-OFF* */ VLIB_REGISTER_NODE (flowprobe_timer_node,static) = { .function = timer_process, .name = "flowprobe-timer-process", .type = VLIB_NODE_TYPE_PROCESS, }; -/* *INDENT-ON* */ #include <flowprobe/flowprobe.api.c> diff --git a/src/plugins/flowprobe/flowprobe.h b/src/plugins/flowprobe/flowprobe.h index 2d28c81de33..1fde5f94491 100644 --- a/src/plugins/flowprobe/flowprobe.h +++ b/src/plugins/flowprobe/flowprobe.h @@ -42,17 +42,22 @@ typedef enum FLOW_N_RECORDS = 1 << 5, } flowprobe_record_t; -/* *INDENT-OFF* */ typedef enum __attribute__ ((__packed__)) { - FLOW_VARIANT_IP4, + FLOW_VARIANT_IP4 = 0, FLOW_VARIANT_IP6, FLOW_VARIANT_L2, FLOW_VARIANT_L2_IP4, FLOW_VARIANT_L2_IP6, FLOW_N_VARIANTS, } flowprobe_variant_t; -/* *INDENT-ON* */ + +typedef enum __attribute__ ((__packed__)) +{ + FLOW_DIRECTION_RX = 0, + FLOW_DIRECTION_TX, + FLOW_DIRECTION_BOTH, +} flowprobe_direction_t; STATIC_ASSERT (sizeof (flowprobe_variant_t) == 1, "flowprobe_variant_t is expected to be 1 byte, " @@ -72,7 +77,6 @@ typedef struct u16 *next_record_offset_per_worker; } flowprobe_protocol_context_t; -/* *INDENT-OFF* */ typedef struct __attribute__ ((aligned (8))) { u32 rx_sw_if_index; u32 tx_sw_if_index; @@ -85,8 +89,8 @@ typedef struct __attribute__ ((aligned (8))) { u16 src_port; u16 dst_port; flowprobe_variant_t which; + flowprobe_direction_t direction; } flowprobe_key_t; -/* *INDENT-ON* */ typedef struct { @@ -134,9 +138,7 @@ typedef struct u8 ht_log2len; /* Hash table size is 2^log2len */ u32 **hash_per_worker; flowprobe_entry_t **pool_per_worker; - /* *INDENT-OFF* */ TWT (tw_timer_wheel) ** timers_per_worker; - /* *INDENT-ON* */ u32 **expired_passive_per_worker; flowprobe_record_t record; @@ -149,6 +151,7 @@ typedef struct u16 template_per_flow[FLOW_N_VARIANTS]; u8 *flow_per_interface; + u8 *direction_per_interface; /** convenience vlib_main_t pointer */ vlib_main_t *vlib_main; @@ -159,6 +162,8 @@ typedef struct extern flowprobe_main_t flowprobe_main; extern vlib_node_registration_t flowprobe_walker_node; +void flowprobe_delete_by_index (u32 my_cpu_number, u32 poolindex); + void flowprobe_flush_callback_ip4 (void); void flowprobe_flush_callback_ip6 (void); void flowprobe_flush_callback_l2 (void); diff --git a/src/plugins/flowprobe/flowprobe_plugin_doc.md b/src/plugins/flowprobe/flowprobe_plugin_doc.md deleted file mode 100644 index 4c9b2342a83..00000000000 --- a/src/plugins/flowprobe/flowprobe_plugin_doc.md +++ /dev/null @@ -1,13 +0,0 @@ -IPFIX flow record plugin {#flowprobe_plugin_doc} -======================== - -## Introduction - -This plugin generates ipfix flow records on interfaces which have the feature enabled - -## Sample configuration - -set ipfix exporter collector 192.168.6.2 src 192.168.6.1 template-interval 20 port 4739 path-mtu 1500 - -flowprobe params record l3 active 20 passive 120 -flowprobe feature add-del GigabitEthernet2/3/0 l2
\ No newline at end of file diff --git a/src/plugins/flowprobe/flowprobe_plugin_doc.rst b/src/plugins/flowprobe/flowprobe_plugin_doc.rst new file mode 100644 index 00000000000..4add41f5611 --- /dev/null +++ b/src/plugins/flowprobe/flowprobe_plugin_doc.rst @@ -0,0 +1,18 @@ +IPFIX flow record plugin +======================== + +Introduction +------------ + +This plugin generates ipfix flow records on interfaces which have the +feature enabled + +Sample configuration +-------------------- + +:: + + set ipfix exporter collector 192.168.6.2 src 192.168.6.1 template-interval 20 port 4739 path-mtu 1450 + + flowprobe params record l3 active 20 passive 120 + flowprobe feature add-del GigabitEthernet2/3/0 l2 diff --git a/src/plugins/flowprobe/flowprobe_test.c b/src/plugins/flowprobe/flowprobe_test.c index a694e45ae9b..37b91207e29 100644 --- a/src/plugins/flowprobe/flowprobe_test.c +++ b/src/plugins/flowprobe/flowprobe_test.c @@ -93,6 +93,136 @@ api_flowprobe_tx_interface_add_del (vat_main_t * vam) } static int +api_flowprobe_interface_add_del (vat_main_t *vam) +{ + unformat_input_t *i = vam->input; + int enable_disable = 1; + u8 which = FLOWPROBE_WHICH_IP4; + u8 direction = FLOWPROBE_DIRECTION_TX; + u32 sw_if_index = ~0; + vl_api_flowprobe_interface_add_del_t *mp; + int ret; + + /* Parse args required to build the message */ + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index)) + ; + else if (unformat (i, "sw_if_index %d", &sw_if_index)) + ; + else if (unformat (i, "disable")) + enable_disable = 0; + else if (unformat (i, "ip4")) + which = FLOWPROBE_WHICH_IP4; + else if (unformat (i, "ip6")) + which = FLOWPROBE_WHICH_IP6; + else if (unformat (i, "l2")) + which = FLOWPROBE_WHICH_L2; + else if (unformat (i, "rx")) + direction = FLOWPROBE_DIRECTION_RX; + else if (unformat (i, "tx")) + direction = FLOWPROBE_DIRECTION_TX; + else if (unformat (i, "both")) + direction = FLOWPROBE_DIRECTION_BOTH; + else + break; + } + + if (sw_if_index == ~0) + { + errmsg ("Missing interface name / explicit sw_if_index number\n"); + return -99; + } + + /* Construct the API message */ + M (FLOWPROBE_INTERFACE_ADD_DEL, mp); + mp->sw_if_index = ntohl (sw_if_index); + mp->is_add = enable_disable; + mp->which = which; + mp->direction = direction; + + /* Send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + return ret; +} + +static int +api_flowprobe_interface_dump (vat_main_t *vam) +{ + unformat_input_t *i = vam->input; + vl_api_flowprobe_interface_dump_t *mp; + vl_api_control_ping_t *mp_ping; + u32 sw_if_index = ~0; + int ret; + + /* Parse args required to build the message */ + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "%d", &sw_if_index)) + ; + else + break; + } + + /* Construct the API message */ + M (FLOWPROBE_INTERFACE_DUMP, mp); + mp->sw_if_index = htonl (sw_if_index); + + /* Send it... */ + S (mp); + + /* Use control ping for synchronization */ + PING (&flowprobe_test_main, mp_ping); + S (mp_ping); + + /* Wait for a reply... */ + W (ret); + return ret; +} + +static void +vl_api_flowprobe_interface_details_t_handler ( + vl_api_flowprobe_interface_details_t *mp) +{ + vat_main_t *vam = flowprobe_test_main.vat_main; + u32 sw_if_index; + u8 which; + u8 direction; + u8 *out = 0; + const char *variants[] = { + [FLOWPROBE_WHICH_IP4] = "ip4", + [FLOWPROBE_WHICH_IP6] = "ip6", + [FLOWPROBE_WHICH_L2] = "l2", + "Erroneous variant", + }; + const char *directions[] = { + [FLOWPROBE_DIRECTION_RX] = "rx", + [FLOWPROBE_DIRECTION_TX] = "tx", + [FLOWPROBE_DIRECTION_BOTH] = "rx tx", + "Erroneous direction", + }; + + sw_if_index = ntohl (mp->sw_if_index); + + which = mp->which; + if (which > ARRAY_LEN (variants) - 2) + which = ARRAY_LEN (variants) - 1; + + direction = mp->direction; + if (direction > ARRAY_LEN (directions) - 2) + direction = ARRAY_LEN (directions) - 1; + + out = format (0, "sw_if_index: %u, variant: %s, direction: %s\n%c", + sw_if_index, variants[which], directions[direction], 0); + + fformat (vam->ofp, (char *) out); + vec_free (out); +} + +static int api_flowprobe_params (vat_main_t * vam) { unformat_input_t *i = vam->input; @@ -145,6 +275,94 @@ api_flowprobe_params (vat_main_t * vam) return ret; } +static int +api_flowprobe_set_params (vat_main_t *vam) +{ + unformat_input_t *i = vam->input; + vl_api_flowprobe_set_params_t *mp; + u32 active_timer = ~0; + u32 passive_timer = ~0; + u8 record_flags = 0; + int ret; + + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "active %d", &active_timer)) + ; + else if (unformat (i, "passive %d", &passive_timer)) + ; + else if (unformat (i, "record")) + while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT) + { + if (unformat (i, "l2")) + record_flags |= FLOWPROBE_RECORD_FLAG_L2; + else if (unformat (i, "l3")) + record_flags |= FLOWPROBE_RECORD_FLAG_L3; + else if (unformat (i, "l4")) + record_flags |= FLOWPROBE_RECORD_FLAG_L4; + else + break; + } + else + break; + } + + /* Construct the API message */ + M (FLOWPROBE_SET_PARAMS, mp); + mp->record_flags = record_flags; + mp->active_timer = ntohl (active_timer); + mp->passive_timer = ntohl (passive_timer); + + /* send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + + return ret; +} + +static int +api_flowprobe_get_params (vat_main_t *vam) +{ + vl_api_flowprobe_get_params_t *mp; + int ret; + + /* Construct the API message */ + M (FLOWPROBE_GET_PARAMS, mp); + + /* Send it... */ + S (mp); + + /* Wait for a reply... */ + W (ret); + return ret; +} + +static void +vl_api_flowprobe_get_params_reply_t_handler ( + vl_api_flowprobe_get_params_reply_t *mp) +{ + vat_main_t *vam = flowprobe_test_main.vat_main; + u8 *out = 0; + + out = + format (0, "active: %u, passive: %u, record:", ntohl (mp->active_timer), + ntohl (mp->passive_timer)); + + if (mp->record_flags & FLOWPROBE_RECORD_FLAG_L2) + out = format (out, " l2"); + if (mp->record_flags & FLOWPROBE_RECORD_FLAG_L3) + out = format (out, " l3"); + if (mp->record_flags & FLOWPROBE_RECORD_FLAG_L4) + out = format (out, " l4"); + + out = format (out, "\n%c", 0); + fformat (vam->ofp, (char *) out); + vec_free (out); + vam->result_ready = 1; +} + /* * List of messages that the api test plugin sends, * and that the data plane plugin processes diff --git a/src/plugins/flowprobe/node.c b/src/plugins/flowprobe/node.c index e7a39a7ed7e..03511689dda 100644 --- a/src/plugins/flowprobe/node.c +++ b/src/plugins/flowprobe/node.c @@ -17,6 +17,7 @@ #include <vlib/vlib.h> #include <vnet/vnet.h> #include <vppinfra/crc32.h> +#include <vppinfra/xxhash.h> #include <vppinfra/error.h> #include <flowprobe/flowprobe.h> #include <vnet/ip/ip6_packet.h> @@ -98,9 +99,15 @@ format_flowprobe_trace (u8 * s, va_list * args) return s; } -vlib_node_registration_t flowprobe_ip4_node; -vlib_node_registration_t flowprobe_ip6_node; -vlib_node_registration_t flowprobe_l2_node; +vlib_node_registration_t flowprobe_input_ip4_node; +vlib_node_registration_t flowprobe_input_ip6_node; +vlib_node_registration_t flowprobe_input_l2_node; +vlib_node_registration_t flowprobe_output_ip4_node; +vlib_node_registration_t flowprobe_output_ip6_node; +vlib_node_registration_t flowprobe_output_l2_node; +vlib_node_registration_t flowprobe_flush_ip4_node; +vlib_node_registration_t flowprobe_flush_ip6_node; +vlib_node_registration_t flowprobe_flush_l2_node; /* No counters at the moment */ #define foreach_flowprobe_error \ @@ -166,6 +173,11 @@ flowprobe_common_add (vlib_buffer_t * to_b, flowprobe_entry_t * e, u16 offset) clib_memcpy_fast (to_b->data + offset, &tx_if, sizeof (tx_if)); offset += sizeof (tx_if); + /* Flow direction + 0x00: ingress flow + 0x01: egress flow */ + to_b->data[offset++] = (e->key.direction == FLOW_DIRECTION_TX); + /* packet delta count */ u64 packetdelta = clib_host_to_net_u64 (e->packetcount); clib_memcpy_fast (to_b->data + offset, &packetdelta, sizeof (u64)); @@ -357,25 +369,30 @@ flowprobe_create (u32 my_cpu_number, flowprobe_key_t * k, u32 * poolindex) } static inline void -add_to_flow_record_state (vlib_main_t * vm, vlib_node_runtime_t * node, - flowprobe_main_t * fm, vlib_buffer_t * b, +add_to_flow_record_state (vlib_main_t *vm, vlib_node_runtime_t *node, + flowprobe_main_t *fm, vlib_buffer_t *b, timestamp_nsec_t timestamp, u16 length, - flowprobe_variant_t which, flowprobe_trace_t * t) + flowprobe_variant_t which, + flowprobe_direction_t direction, + flowprobe_trace_t *t) { if (fm->disabled) return; + ASSERT (direction == FLOW_DIRECTION_RX || direction == FLOW_DIRECTION_TX); + u32 my_cpu_number = vm->thread_index; u16 octets = 0; flowprobe_record_t flags = fm->context[which].flags; bool collect_ip4 = false, collect_ip6 = false; ASSERT (b); - ethernet_header_t *eth = vlib_buffer_get_current (b); + ethernet_header_t *eth = (direction == FLOW_DIRECTION_TX) ? + vlib_buffer_get_current (b) : + ethernet_buffer_get_header (b); u16 ethertype = clib_net_to_host_u16 (eth->type); - /* *INDENT-OFF* */ + i16 l3_hdr_offset = (u8 *) eth - b->data + sizeof (ethernet_header_t); flowprobe_key_t k = {}; - /* *INDENT-ON* */ ip4_header_t *ip4 = 0; ip6_header_t *ip6 = 0; udp_header_t *udp = 0; @@ -392,6 +409,7 @@ add_to_flow_record_state (vlib_main_t * vm, vlib_node_runtime_t * node, k.tx_sw_if_index = vnet_buffer (b)->sw_if_index[VLIB_TX]; k.which = which; + k.direction = direction; if (flags & FLOW_RECORD_L2) { @@ -399,9 +417,22 @@ add_to_flow_record_state (vlib_main_t * vm, vlib_node_runtime_t * node, clib_memcpy_fast (k.dst_mac, eth->dst_address, 6); k.ethertype = ethertype; } + if (ethertype == ETHERNET_TYPE_VLAN) + { + /*VLAN TAG*/ + ethernet_vlan_header_tv_t *ethv = + (ethernet_vlan_header_tv_t *) (&(eth->type)); + /*Q in Q possibility */ + while (clib_net_to_host_u16 (ethv->type) == ETHERNET_TYPE_VLAN) + { + ethv++; + l3_hdr_offset += sizeof (ethernet_vlan_header_tv_t); + } + k.ethertype = ethertype = clib_net_to_host_u16 ((ethv)->type); + } if (collect_ip6 && ethertype == ETHERNET_TYPE_IP6) { - ip6 = (ip6_header_t *) (eth + 1); + ip6 = (ip6_header_t *) (b->data + l3_hdr_offset); if (flags & FLOW_RECORD_L3) { k.src_address.as_u64[0] = ip6->src_address.as_u64[0]; @@ -420,7 +451,7 @@ add_to_flow_record_state (vlib_main_t * vm, vlib_node_runtime_t * node, } if (collect_ip4 && ethertype == ETHERNET_TYPE_IP4) { - ip4 = (ip4_header_t *) (eth + 1); + ip4 = (ip4_header_t *) (b->data + l3_hdr_offset); if (flags & FLOW_RECORD_L3) { k.src_address.ip4.as_u32 = ip4->src_address.as_u32; @@ -520,6 +551,7 @@ flowprobe_export_send (vlib_main_t * vm, vlib_buffer_t * b0, { flowprobe_main_t *fm = &flowprobe_main; flow_report_main_t *frm = &flow_report_main; + ipfix_exporter_t *exp = pool_elt_at_index (frm->exporters, 0); vlib_frame_t *f; ip4_ipfix_template_packet_t *tp; ipfix_set_header_t *s; @@ -537,19 +569,19 @@ flowprobe_export_send (vlib_main_t * vm, vlib_buffer_t * b0, flowprobe_get_headersize ()) return; - u32 i, index = vec_len (frm->streams); + u32 i, index = vec_len (exp->streams); for (i = 0; i < index; i++) - if (frm->streams[i].domain_id == 1) + if (exp->streams[i].domain_id == 1) { index = i; break; } - if (i == vec_len (frm->streams)) + if (i == vec_len (exp->streams)) { - vec_validate (frm->streams, index); - frm->streams[index].domain_id = 1; + vec_validate (exp->streams, index); + exp->streams[index].domain_id = 1; } - stream = &frm->streams[index]; + stream = &exp->streams[index]; tp = vlib_buffer_get_current (b0); ip = (ip4_header_t *) & tp->ip4; @@ -561,16 +593,15 @@ flowprobe_export_send (vlib_main_t * vm, vlib_buffer_t * b0, ip->ttl = 254; ip->protocol = IP_PROTOCOL_UDP; ip->flags_and_fragment_offset = 0; - ip->src_address.as_u32 = frm->src_address.as_u32; - ip->dst_address.as_u32 = frm->ipfix_collector.as_u32; + ip->src_address.as_u32 = exp->src_address.ip.ip4.as_u32; + ip->dst_address.as_u32 = exp->ipfix_collector.ip.ip4.as_u32; udp->src_port = clib_host_to_net_u16 (stream->src_port); - udp->dst_port = clib_host_to_net_u16 (frm->collector_port); + udp->dst_port = clib_host_to_net_u16 (exp->collector_port); udp->checksum = 0; /* FIXUP: message header export_time */ - h->export_time = (u32) - (((f64) frm->unix_time_0) + - (vlib_time_now (frm->vlib_main) - frm->vlib_time_0)); + h->export_time = + (u32) (((f64) frm->unix_time_0) + (vlib_time_now (vm) - frm->vlib_time_0)); h->export_time = clib_host_to_net_u32 (h->export_time); h->domain_id = clib_host_to_net_u32 (stream->domain_id); @@ -590,7 +621,7 @@ flowprobe_export_send (vlib_main_t * vm, vlib_buffer_t * b0, ip->checksum = ip4_header_checksum (ip); udp->length = clib_host_to_net_u16 (b0->current_length - sizeof (*ip)); - if (frm->udp_checksum) + if (exp->udp_checksum) { /* RFC 7011 section 10.3.2. */ udp->checksum = ip4_tcp_udp_compute_checksum (vm, b0, ip); @@ -616,7 +647,7 @@ flowprobe_export_send (vlib_main_t * vm, vlib_buffer_t * b0, } vlib_put_frame_to_node (vm, ip4_lookup_node.index, f); - vlib_node_increment_counter (vm, flowprobe_l2_node.index, + vlib_node_increment_counter (vm, flowprobe_output_l2_node.index, FLOWPROBE_ERROR_EXPORTED_PACKETS, 1); fm->context[which].frames_per_worker[my_cpu_number] = 0; @@ -629,7 +660,7 @@ static vlib_buffer_t * flowprobe_get_buffer (vlib_main_t * vm, flowprobe_variant_t which) { flowprobe_main_t *fm = &flowprobe_main; - flow_report_main_t *frm = &flow_report_main; + ipfix_exporter_t *exp = pool_elt_at_index (flow_report_main.exporters, 0); vlib_buffer_t *b0; u32 bi0; u32 my_cpu_number = vm->thread_index; @@ -642,7 +673,7 @@ flowprobe_get_buffer (vlib_main_t * vm, flowprobe_variant_t which) { if (vlib_buffer_alloc (vm, &bi0, 1) != 1) { - vlib_node_increment_counter (vm, flowprobe_l2_node.index, + vlib_node_increment_counter (vm, flowprobe_output_l2_node.index, FLOWPROBE_ERROR_BUFFER, 1); return 0; } @@ -656,7 +687,7 @@ flowprobe_get_buffer (vlib_main_t * vm, flowprobe_variant_t which) b0->flags |= (VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_FLOW_REPORT); vnet_buffer (b0)->sw_if_index[VLIB_RX] = 0; - vnet_buffer (b0)->sw_if_index[VLIB_TX] = frm->fib_index; + vnet_buffer (b0)->sw_if_index[VLIB_TX] = exp->fib_index; fm->context[which].next_record_offset_per_worker[my_cpu_number] = b0->current_length; } @@ -669,9 +700,10 @@ flowprobe_export_entry (vlib_main_t * vm, flowprobe_entry_t * e) { u32 my_cpu_number = vm->thread_index; flowprobe_main_t *fm = &flowprobe_main; - flow_report_main_t *frm = &flow_report_main; + ipfix_exporter_t *exp = pool_elt_at_index (flow_report_main.exporters, 0); vlib_buffer_t *b0; bool collect_ip4 = false, collect_ip6 = false; + bool collect_l4 = false; flowprobe_variant_t which = e->key.which; flowprobe_record_t flags = fm->context[which].flags; u16 offset = @@ -690,6 +722,10 @@ flowprobe_export_entry (vlib_main_t * vm, flowprobe_entry_t * e) collect_ip4 = which == FLOW_VARIANT_L2_IP4 || which == FLOW_VARIANT_IP4; collect_ip6 = which == FLOW_VARIANT_L2_IP6 || which == FLOW_VARIANT_IP6; } + if (flags & FLOW_RECORD_L4) + { + collect_l4 = (which != FLOW_VARIANT_L2); + } offset += flowprobe_common_add (b0, e, offset); @@ -699,26 +735,27 @@ flowprobe_export_entry (vlib_main_t * vm, flowprobe_entry_t * e) offset += flowprobe_l3_ip6_add (b0, e, offset); if (collect_ip4) offset += flowprobe_l3_ip4_add (b0, e, offset); - if (flags & FLOW_RECORD_L4) + if (collect_l4) offset += flowprobe_l4_add (b0, e, offset); /* Reset per flow-export counters */ e->packetcount = 0; e->octetcount = 0; e->last_exported = vlib_time_now (vm); + e->prot.tcp.flags = 0; b0->current_length = offset; fm->context[which].next_record_offset_per_worker[my_cpu_number] = offset; /* Time to flush the buffer? */ - if (offset + fm->template_size[flags] > frm->path_mtu) + if (offset + fm->template_size[flags] > exp->path_mtu) flowprobe_export_send (vm, b0, which); } uword -flowprobe_node_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * frame, - flowprobe_variant_t which) +flowprobe_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame, flowprobe_variant_t which, + flowprobe_direction_t direction) { u32 n_left_from, *from, *to_next; flowprobe_next_t next_index; @@ -778,20 +815,22 @@ flowprobe_node_fn (vlib_main_t * vm, u16 ethertype0 = clib_net_to_host_u16 (eh0->type); if (PREDICT_TRUE ((b0->flags & VNET_BUFFER_F_FLOW_REPORT) == 0)) - add_to_flow_record_state (vm, node, fm, b0, timestamp, len0, - flowprobe_get_variant - (which, fm->context[which].flags, - ethertype0), 0); + add_to_flow_record_state ( + vm, node, fm, b0, timestamp, len0, + flowprobe_get_variant (which, fm->context[which].flags, + ethertype0), + direction, 0); len1 = vlib_buffer_length_in_chain (vm, b1); ethernet_header_t *eh1 = vlib_buffer_get_current (b1); u16 ethertype1 = clib_net_to_host_u16 (eh1->type); if (PREDICT_TRUE ((b1->flags & VNET_BUFFER_F_FLOW_REPORT) == 0)) - add_to_flow_record_state (vm, node, fm, b1, timestamp, len1, - flowprobe_get_variant - (which, fm->context[which].flags, - ethertype1), 0); + add_to_flow_record_state ( + vm, node, fm, b1, timestamp, len1, + flowprobe_get_variant (which, fm->context[which].flags, + ethertype1), + direction, 0); /* verify speculative enqueues, maybe switch current next frame */ vlib_validate_buffer_enqueue_x2 (vm, node, next_index, @@ -829,10 +868,11 @@ flowprobe_node_fn (vlib_main_t * vm, && (b0->flags & VLIB_BUFFER_IS_TRACED))) t = vlib_add_trace (vm, node, b0, sizeof (*t)); - add_to_flow_record_state (vm, node, fm, b0, timestamp, len0, - flowprobe_get_variant - (which, fm->context[which].flags, - ethertype0), t); + add_to_flow_record_state ( + vm, node, fm, b0, timestamp, len0, + flowprobe_get_variant (which, fm->context[which].flags, + ethertype0), + direction, t); } /* verify speculative enqueue, maybe switch current next frame */ @@ -847,24 +887,51 @@ flowprobe_node_fn (vlib_main_t * vm, } static uword -flowprobe_ip4_node_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * frame) +flowprobe_input_ip4_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame) +{ + return flowprobe_node_fn (vm, node, frame, FLOW_VARIANT_IP4, + FLOW_DIRECTION_RX); +} + +static uword +flowprobe_input_ip6_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame) +{ + return flowprobe_node_fn (vm, node, frame, FLOW_VARIANT_IP6, + FLOW_DIRECTION_RX); +} + +static uword +flowprobe_input_l2_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame) +{ + return flowprobe_node_fn (vm, node, frame, FLOW_VARIANT_L2, + FLOW_DIRECTION_RX); +} + +static uword +flowprobe_output_ip4_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame) { - return flowprobe_node_fn (vm, node, frame, FLOW_VARIANT_IP4); + return flowprobe_node_fn (vm, node, frame, FLOW_VARIANT_IP4, + FLOW_DIRECTION_TX); } static uword -flowprobe_ip6_node_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * frame) +flowprobe_output_ip6_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame) { - return flowprobe_node_fn (vm, node, frame, FLOW_VARIANT_IP6); + return flowprobe_node_fn (vm, node, frame, FLOW_VARIANT_IP6, + FLOW_DIRECTION_TX); } static uword -flowprobe_l2_node_fn (vlib_main_t * vm, - vlib_node_runtime_t * node, vlib_frame_t * frame) +flowprobe_output_l2_node_fn (vlib_main_t *vm, vlib_node_runtime_t *node, + vlib_frame_t *frame) { - return flowprobe_node_fn (vm, node, frame, FLOW_VARIANT_L2); + return flowprobe_node_fn (vm, node, frame, FLOW_VARIANT_L2, + FLOW_DIRECTION_TX); } static inline void @@ -879,25 +946,63 @@ flush_record (flowprobe_variant_t which) void flowprobe_flush_callback_ip4 (void) { + vlib_main_t *worker_vm; + u32 i; + + /* Flush for each worker thread */ + for (i = 1; i < vlib_get_n_threads (); i++) + { + worker_vm = vlib_get_main_by_index (i); + if (worker_vm) + vlib_node_set_interrupt_pending (worker_vm, + flowprobe_flush_ip4_node.index); + } + + /* Flush for the main thread */ flush_record (FLOW_VARIANT_IP4); } void flowprobe_flush_callback_ip6 (void) { + vlib_main_t *worker_vm; + u32 i; + + /* Flush for each worker thread */ + for (i = 1; i < vlib_get_n_threads (); i++) + { + worker_vm = vlib_get_main_by_index (i); + if (worker_vm) + vlib_node_set_interrupt_pending (worker_vm, + flowprobe_flush_ip6_node.index); + } + + /* Flush for the main thread */ flush_record (FLOW_VARIANT_IP6); } void flowprobe_flush_callback_l2 (void) { + vlib_main_t *worker_vm; + u32 i; + + /* Flush for each worker thread */ + for (i = 1; i < vlib_get_n_threads (); i++) + { + worker_vm = vlib_get_main_by_index (i); + if (worker_vm) + vlib_node_set_interrupt_pending (worker_vm, + flowprobe_flush_l2_node.index); + } + + /* Flush for the main thread */ flush_record (FLOW_VARIANT_L2); flush_record (FLOW_VARIANT_L2_IP4); flush_record (FLOW_VARIANT_L2_IP6); } - -static void +void flowprobe_delete_by_index (u32 my_cpu_number, u32 poolindex) { flowprobe_main_t *fm = &flowprobe_main; @@ -922,14 +1027,15 @@ flowprobe_walker_process (vlib_main_t * vm, vlib_node_runtime_t * rt, vlib_frame_t * f) { flowprobe_main_t *fm = &flowprobe_main; - flow_report_main_t *frm = &flow_report_main; flowprobe_entry_t *e; + ipfix_exporter_t *exp = pool_elt_at_index (flow_report_main.exporters, 0); /* * $$$$ Remove this check from here and track FRM status and disable * this process if required. */ - if (frm->ipfix_collector.as_u32 == 0 || frm->src_address.as_u32 == 0) + if (ip_address_is_zero (&exp->ipfix_collector) || + ip_address_is_zero (&exp->src_address)) { fm->disabled = true; return 0; @@ -996,36 +1102,94 @@ flowprobe_walker_process (vlib_main_t * vm, return 0; } -/* *INDENT-OFF* */ -VLIB_REGISTER_NODE (flowprobe_ip4_node) = { - .function = flowprobe_ip4_node_fn, - .name = "flowprobe-ip4", +static uword +flowprobe_flush_ip4 (vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f) +{ + flush_record (FLOW_VARIANT_IP4); + + return 0; +} + +static uword +flowprobe_flush_ip6 (vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f) +{ + flush_record (FLOW_VARIANT_IP6); + + return 0; +} + +static uword +flowprobe_flush_l2 (vlib_main_t *vm, vlib_node_runtime_t *rt, vlib_frame_t *f) +{ + flush_record (FLOW_VARIANT_L2); + flush_record (FLOW_VARIANT_L2_IP4); + flush_record (FLOW_VARIANT_L2_IP6); + + return 0; +} + +VLIB_REGISTER_NODE (flowprobe_input_ip4_node) = { + .function = flowprobe_input_ip4_node_fn, + .name = "flowprobe-input-ip4", + .vector_size = sizeof (u32), + .format_trace = format_flowprobe_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (flowprobe_error_strings), + .error_strings = flowprobe_error_strings, + .n_next_nodes = FLOWPROBE_N_NEXT, + .next_nodes = FLOWPROBE_NEXT_NODES, +}; +VLIB_REGISTER_NODE (flowprobe_input_ip6_node) = { + .function = flowprobe_input_ip6_node_fn, + .name = "flowprobe-input-ip6", .vector_size = sizeof (u32), .format_trace = format_flowprobe_trace, .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(flowprobe_error_strings), + .n_errors = ARRAY_LEN (flowprobe_error_strings), .error_strings = flowprobe_error_strings, .n_next_nodes = FLOWPROBE_N_NEXT, .next_nodes = FLOWPROBE_NEXT_NODES, }; -VLIB_REGISTER_NODE (flowprobe_ip6_node) = { - .function = flowprobe_ip6_node_fn, - .name = "flowprobe-ip6", +VLIB_REGISTER_NODE (flowprobe_input_l2_node) = { + .function = flowprobe_input_l2_node_fn, + .name = "flowprobe-input-l2", .vector_size = sizeof (u32), .format_trace = format_flowprobe_trace, .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(flowprobe_error_strings), + .n_errors = ARRAY_LEN (flowprobe_error_strings), .error_strings = flowprobe_error_strings, .n_next_nodes = FLOWPROBE_N_NEXT, .next_nodes = FLOWPROBE_NEXT_NODES, }; -VLIB_REGISTER_NODE (flowprobe_l2_node) = { - .function = flowprobe_l2_node_fn, - .name = "flowprobe-l2", +VLIB_REGISTER_NODE (flowprobe_output_ip4_node) = { + .function = flowprobe_output_ip4_node_fn, + .name = "flowprobe-output-ip4", .vector_size = sizeof (u32), .format_trace = format_flowprobe_trace, .type = VLIB_NODE_TYPE_INTERNAL, - .n_errors = ARRAY_LEN(flowprobe_error_strings), + .n_errors = ARRAY_LEN (flowprobe_error_strings), + .error_strings = flowprobe_error_strings, + .n_next_nodes = FLOWPROBE_N_NEXT, + .next_nodes = FLOWPROBE_NEXT_NODES, +}; +VLIB_REGISTER_NODE (flowprobe_output_ip6_node) = { + .function = flowprobe_output_ip6_node_fn, + .name = "flowprobe-output-ip6", + .vector_size = sizeof (u32), + .format_trace = format_flowprobe_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (flowprobe_error_strings), + .error_strings = flowprobe_error_strings, + .n_next_nodes = FLOWPROBE_N_NEXT, + .next_nodes = FLOWPROBE_NEXT_NODES, +}; +VLIB_REGISTER_NODE (flowprobe_output_l2_node) = { + .function = flowprobe_output_l2_node_fn, + .name = "flowprobe-output-l2", + .vector_size = sizeof (u32), + .format_trace = format_flowprobe_trace, + .type = VLIB_NODE_TYPE_INTERNAL, + .n_errors = ARRAY_LEN (flowprobe_error_strings), .error_strings = flowprobe_error_strings, .n_next_nodes = FLOWPROBE_N_NEXT, .next_nodes = FLOWPROBE_NEXT_NODES, @@ -1036,7 +1200,24 @@ VLIB_REGISTER_NODE (flowprobe_walker_node) = { .type = VLIB_NODE_TYPE_INPUT, .state = VLIB_NODE_STATE_INTERRUPT, }; -/* *INDENT-ON* */ +VLIB_REGISTER_NODE (flowprobe_flush_ip4_node) = { + .function = flowprobe_flush_ip4, + .name = "flowprobe-flush-ip4", + .type = VLIB_NODE_TYPE_INPUT, + .state = VLIB_NODE_STATE_INTERRUPT, +}; +VLIB_REGISTER_NODE (flowprobe_flush_ip6_node) = { + .function = flowprobe_flush_ip6, + .name = "flowprobe-flush-ip6", + .type = VLIB_NODE_TYPE_INPUT, + .state = VLIB_NODE_STATE_INTERRUPT, +}; +VLIB_REGISTER_NODE (flowprobe_flush_l2_node) = { + .function = flowprobe_flush_l2, + .name = "flowprobe-flush-l2", + .type = VLIB_NODE_TYPE_INPUT, + .state = VLIB_NODE_STATE_INTERRUPT, +}; /* * fd.io coding-style-patch-verification: ON |