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>
This commit is contained in:
Alexander Chernavin
2022-05-12 14:56:24 +00:00
committed by Matthew Smith
parent 99b9639845
commit 86c7856eda
4 changed files with 475 additions and 7 deletions

View File

@ -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;
};

View File

@ -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) = {

View File

@ -149,6 +149,79 @@ api_flowprobe_interface_add_del (vat_main_t *vam)
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)
{
@ -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

View File

@ -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)"""