diff options
-rw-r--r-- | src/plugins/flowprobe/flowprobe.api | 83 | ||||
-rw-r--r-- | src/plugins/flowprobe/flowprobe.c | 145 | ||||
-rw-r--r-- | src/plugins/flowprobe/flowprobe_test.c | 161 | ||||
-rw-r--r-- | test/test_flowprobe.py | 93 |
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)""" |