aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Chernavin <achernavin@netgate.com>2022-05-12 14:56:24 +0000
committerMatthew Smith <mgsmith@netgate.com>2022-05-16 20:53:19 +0000
commit86c7856edae44483e7303fb4cf4efdec626716c8 (patch)
treefb30caa5131691d52b610543b9b7815d8e7489a0
parent99b96398452ac13114d54f47a163dc06f7c23f12 (diff)
flowprobe: add api messages to obtain current state
Type: improvement With this change: - add dump/details messages to obtain interfaces for which IPFIX flow record generation is enabled; - add get message to obtain parameters; - add a new message to set parameters with validation present and to correspond with get/set naming; - add tests for get/set parameters and dump/details interfaces. Change-Id: I09f6ec990171ac8bcb9d2f5c92629803b8ab6c28 Signed-off-by: Alexander Chernavin <achernavin@netgate.com>
-rw-r--r--src/plugins/flowprobe/flowprobe.api83
-rw-r--r--src/plugins/flowprobe/flowprobe.c145
-rw-r--r--src/plugins/flowprobe/flowprobe_test.c161
-rw-r--r--test/test_flowprobe.py93
4 files changed, 475 insertions, 7 deletions
diff --git a/src/plugins/flowprobe/flowprobe.api b/src/plugins/flowprobe/flowprobe.api
index 8702568c7ea..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 = "2.0.0";
+option version = "2.1.0";
import "vnet/interface_types.api";
@@ -83,8 +83,39 @@ autoreply define flowprobe_interface_add_del
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;
@@ -92,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 5a747a61c73..df0e5ff0f2b 100644
--- a/src/plugins/flowprobe/flowprobe.c
+++ b/src/plugins/flowprobe/flowprobe.c
@@ -772,6 +772,82 @@ out:
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; \
@@ -792,7 +868,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;
@@ -832,6 +908,65 @@ vl_api_flowprobe_params_t_handler (vl_api_flowprobe_params_t * mp)
REPLY_MACRO (VL_API_FLOWPROBE_PARAMS_REPLY);
}
+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
+}
+
/* *INDENT-OFF* */
VLIB_PLUGIN_REGISTER () = {
.version = VPP_BUILD_VER,
@@ -1138,10 +1273,10 @@ VLIB_CLI_COMMAND (flowprobe_enable_disable_command, static) = {
.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) = {
diff --git a/src/plugins/flowprobe/flowprobe_test.c b/src/plugins/flowprobe/flowprobe_test.c
index ae2a3edf64a..37b91207e29 100644
--- a/src/plugins/flowprobe/flowprobe_test.c
+++ b/src/plugins/flowprobe/flowprobe_test.c
@@ -150,6 +150,79 @@ api_flowprobe_interface_add_del (vat_main_t *vam)
}
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;
@@ -202,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/test/test_flowprobe.py b/test/test_flowprobe.py
index 1d86d19ac5a..141d7458c39 100644
--- a/test/test_flowprobe.py
+++ b/test/test_flowprobe.py
@@ -67,7 +67,7 @@ class VppCFLOW(VppObject):
l3_flag = VppEnum.vl_api_flowprobe_record_flags_t.FLOWPROBE_RECORD_FLAG_L3
if "l4" in self._collect.lower():
l4_flag = VppEnum.vl_api_flowprobe_record_flags_t.FLOWPROBE_RECORD_FLAG_L4
- self._test.vapi.flowprobe_params(
+ self._test.vapi.flowprobe_set_params(
record_flags=(l2_flag | l3_flag | l4_flag),
active_timer=self._active,
passive_timer=self._passive,
@@ -481,6 +481,97 @@ class Flowprobe(MethodHolder):
ipfix.remove_vpp_config()
self.logger.info("FFP_TEST_FINISH_0000")
+ def test_interface_dump(self):
+ """Dump interfaces with IPFIX flow record generation enabled"""
+ self.logger.info("FFP_TEST_START_0003")
+
+ # Enable feature for 3 interfaces
+ ipfix1 = VppCFLOW(test=self, intf="pg1", datapath="l2", direction="rx")
+ ipfix1.add_vpp_config()
+
+ ipfix2 = VppCFLOW(test=self, intf="pg2", datapath="ip4", direction="tx")
+ ipfix2.enable_flowprobe_feature()
+
+ ipfix3 = VppCFLOW(test=self, intf="pg3", datapath="ip6", direction="both")
+ ipfix3.enable_flowprobe_feature()
+
+ # When request "all", dump should contain all enabled interfaces
+ dump = self.vapi.flowprobe_interface_dump()
+ self.assertEqual(len(dump), 3)
+
+ # Verify 1st interface
+ self.assertEqual(dump[0].sw_if_index, self.pg1.sw_if_index)
+ self.assertEqual(
+ dump[0].which, VppEnum.vl_api_flowprobe_which_t.FLOWPROBE_WHICH_L2
+ )
+ self.assertEqual(
+ dump[0].direction,
+ VppEnum.vl_api_flowprobe_direction_t.FLOWPROBE_DIRECTION_RX,
+ )
+
+ # Verify 2nd interface
+ self.assertEqual(dump[1].sw_if_index, self.pg2.sw_if_index)
+ self.assertEqual(
+ dump[1].which, VppEnum.vl_api_flowprobe_which_t.FLOWPROBE_WHICH_IP4
+ )
+ self.assertEqual(
+ dump[1].direction,
+ VppEnum.vl_api_flowprobe_direction_t.FLOWPROBE_DIRECTION_TX,
+ )
+
+ # Verify 3rd interface
+ self.assertEqual(dump[2].sw_if_index, self.pg3.sw_if_index)
+ self.assertEqual(
+ dump[2].which, VppEnum.vl_api_flowprobe_which_t.FLOWPROBE_WHICH_IP6
+ )
+ self.assertEqual(
+ dump[2].direction,
+ VppEnum.vl_api_flowprobe_direction_t.FLOWPROBE_DIRECTION_BOTH,
+ )
+
+ # When request 2nd interface, dump should contain only the specified interface
+ dump = self.vapi.flowprobe_interface_dump(sw_if_index=self.pg2.sw_if_index)
+ self.assertEqual(len(dump), 1)
+
+ # Verify 2nd interface
+ self.assertEqual(dump[0].sw_if_index, self.pg2.sw_if_index)
+ self.assertEqual(
+ dump[0].which, VppEnum.vl_api_flowprobe_which_t.FLOWPROBE_WHICH_IP4
+ )
+ self.assertEqual(
+ dump[0].direction,
+ VppEnum.vl_api_flowprobe_direction_t.FLOWPROBE_DIRECTION_TX,
+ )
+
+ # When request 99th interface, dump should be empty
+ dump = self.vapi.flowprobe_interface_dump(sw_if_index=99)
+ self.assertEqual(len(dump), 0)
+
+ ipfix1.remove_vpp_config()
+ ipfix2.remove_vpp_config()
+ ipfix3.remove_vpp_config()
+ self.logger.info("FFP_TEST_FINISH_0003")
+
+ def test_get_params(self):
+ """Get IPFIX flow record generation parameters"""
+ self.logger.info("FFP_TEST_START_0004")
+
+ # Enable feature for an interface with custom parameters
+ ipfix = VppCFLOW(test=self, active=20, passive=40, layer="l2 l3 l4")
+ ipfix.add_vpp_config()
+
+ # Get and verify parameters
+ params = self.vapi.flowprobe_get_params()
+ self.assertEqual(params.active_timer, 20)
+ self.assertEqual(params.passive_timer, 40)
+ record_flags = VppEnum.vl_api_flowprobe_record_flags_t.FLOWPROBE_RECORD_FLAG_L2
+ record_flags |= VppEnum.vl_api_flowprobe_record_flags_t.FLOWPROBE_RECORD_FLAG_L3
+ record_flags |= VppEnum.vl_api_flowprobe_record_flags_t.FLOWPROBE_RECORD_FLAG_L4
+ self.assertEqual(params.record_flags, record_flags)
+
+ ipfix.remove_vpp_config()
+ self.logger.info("FFP_TEST_FINISH_0004")
+
class DatapathTestsHolder(object):
"""collect information on Ethernet, IP4 and IP6 datapath (no timers)"""