diff --git a/CMakeLists.txt b/CMakeLists.txt index 17e6de9d..dee69e15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,6 +86,7 @@ add_subdirectory(pkg) if (ENABLE_TESTS) enable_testing() add_subdirectory(tests/unit) + add_subdirectory(tests/modules) endif() # ------------------------------------------------------------------------------ diff --git a/tests/modules/CMakeLists.txt b/tests/modules/CMakeLists.txt new file mode 100644 index 00000000..07239eee --- /dev/null +++ b/tests/modules/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(input_failure) +add_subdirectory(intermediate_failure) +add_subdirectory(output_failure) \ No newline at end of file diff --git a/tests/modules/input_failure/CMakeLists.txt b/tests/modules/input_failure/CMakeLists.txt new file mode 100644 index 00000000..2b403d42 --- /dev/null +++ b/tests/modules/input_failure/CMakeLists.txt @@ -0,0 +1,6 @@ +# Create a linkable module +add_library(dummy-failure-input MODULE + dummy.c + config.c + config.h +) \ No newline at end of file diff --git a/tests/modules/input_failure/config.c b/tests/modules/input_failure/config.c new file mode 100644 index 00000000..24c78552 --- /dev/null +++ b/tests/modules/input_failure/config.c @@ -0,0 +1,192 @@ +/** + * \file src/plugins/input/dummy/config.c + * \author Lukas Hutak + * \brief Example parser of an XML configuration (source file) + * \date 2018 + */ + +/* Copyright (C) 2018 CESNET, z.s.p.o. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the Company nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * ALTERNATIVELY, provided that this notice is retained in full, this + * product may be distributed under the terms of the GNU General Public + * License (GPL) version 2 or later, in which case the provisions + * of the GPL apply INSTEAD OF those given above. + * + * This software is provided ``as is'', and any express or implied + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose are disclaimed. + * In no event shall the company or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + * + */ + +#include +#include +#include +#include "config.h" + +/* + * + * ... + * ... + * ... + * ... + * + */ + +/** XML nodes */ +enum params_xml_nodes { + NODE_ODID = 1, + NODE_DELAY, + NODE_FAIL_AFTER, + NODE_FAIL_TYPE +}; + +/** Definition of the \ node */ +static const struct fds_xml_args args_params[] = { + FDS_OPTS_ROOT("params"), + FDS_OPTS_ELEM(NODE_ODID, "odid", FDS_OPTS_T_UINT, FDS_OPTS_P_OPT), + FDS_OPTS_ELEM(NODE_DELAY, "delay", FDS_OPTS_T_UINT, 0), + FDS_OPTS_ELEM(NODE_FAIL_AFTER, "failAfter", FDS_OPTS_T_UINT, 0), + FDS_OPTS_ELEM(NODE_FAIL_TYPE, "failType", FDS_OPTS_T_STRING, 0), + FDS_OPTS_END +}; + +/** + * \brief Process \ node + * \param[in] ctx Plugin context + * \param[in] root XML context to process + * \param[in] cfg Parsed configuration + * \return #IPX_OK on success + * \return #IPX_ERR_FORMAT in case of failure + */ +static int +config_parser_root(ipx_ctx_t *ctx, fds_xml_ctx_t *root, struct instance_config *cfg) +{ + const struct fds_xml_cont *content; + while(fds_xml_next(root, &content) != FDS_EOC) { + switch (content->id) { + case NODE_ODID: + // Observation Domain ID + assert(content->type == FDS_OPTS_T_UINT); + if (content->val_uint > UINT32_MAX) { + IPX_CTX_ERROR(ctx, "The ODID value must be between 0 .. 2^32", '\0'); + return IPX_ERR_FORMAT; + } + cfg->odid = (uint32_t) content->val_uint; + break; + case NODE_DELAY: + // Delay between messages [microseconds] + assert(content->type == FDS_OPTS_T_UINT); + cfg->sleep_time.tv_nsec = (content->val_uint % 1000000LL) * 1000LL; + cfg->sleep_time.tv_sec = content->val_uint / 1000000LL; + break; + case NODE_FAIL_AFTER: + // Fail after generating specific number of messages + assert(content->type == FDS_OPTS_T_UINT); + cfg->fail_after = content->val_uint; + break; + case NODE_FAIL_TYPE: + assert(content->type == FDS_OPTS_T_STRING); + if (strcasecmp(content->ptr_string, "IPX_ERR_DENIED") == 0) { + cfg->fail_type = IPX_ERR_DENIED; + } else if (strcasecmp(content->ptr_string, "IPX_ERR_EOF") == 0) { + cfg->fail_type = IPX_ERR_EOF; + } else { + IPX_CTX_ERROR(ctx, "Invalid failure type (expected: IPX_ERR_EOF/IPX_ERR_DENIED", '\0'); + return IPX_ERR_FORMAT; + } + break; + default: + // Internal error + assert(false); + } + } + + return IPX_OK; +} + +/** + * \brief Set default parameters of the configuration + * \param[in] cfg Configuration + */ +static void +config_default_set(struct instance_config *cfg) +{ + cfg->sleep_time.tv_sec = 0; + cfg->sleep_time.tv_nsec = 100000000LL; // 100ms between messages + cfg->odid = 1; + cfg->fail_type = IPX_ERR_EOF; + cfg->fail_after = 0; +} + +struct instance_config * +config_parse(ipx_ctx_t *ctx, const char *params) +{ + struct instance_config *cfg = calloc(1, sizeof(*cfg)); + if (!cfg) { + IPX_CTX_ERROR(ctx, "Memory allocation error (%s:%d)", __FILE__, __LINE__); + return NULL; + } + + // Set default parameters + config_default_set(cfg); + + // Create an XML parser + fds_xml_t *parser = fds_xml_create(); + if (!parser) { + IPX_CTX_ERROR(ctx, "Memory allocation error (%s:%d)", __FILE__, __LINE__); + free(cfg); + return NULL; + } + + if (fds_xml_set_args(parser, args_params) != IPX_OK) { + IPX_CTX_ERROR(ctx, "Failed to parse the description of an XML document!", '\0'); + fds_xml_destroy(parser); + free(cfg); + return NULL; + } + + fds_xml_ctx_t *params_ctx = fds_xml_parse_mem(parser, params, true); + if (params_ctx == NULL) { + IPX_CTX_ERROR(ctx, "Failed to parse the configuration: %s", fds_xml_last_err(parser)); + fds_xml_destroy(parser); + free(cfg); + return NULL; + } + + // Parse parameters + int rc = config_parser_root(ctx, params_ctx, cfg); + fds_xml_destroy(parser); + if (rc != IPX_OK) { + free(cfg); + return NULL; + } + + return cfg; +} + +void +config_destroy(struct instance_config *cfg) +{ + free(cfg); +} diff --git a/tests/modules/input_failure/config.h b/tests/modules/input_failure/config.h new file mode 100644 index 00000000..46a86cfe --- /dev/null +++ b/tests/modules/input_failure/config.h @@ -0,0 +1,78 @@ +/** + * \file src/plugins/input/dummy/config.h + * \author Lukas Hutak + * \brief Example parser of an XML configuration (header file) + * \date 2018 + */ + +/* Copyright (C) 2018 CESNET, z.s.p.o. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the Company nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * ALTERNATIVELY, provided that this notice is retained in full, this + * product may be distributed under the terms of the GNU General Public + * License (GPL) version 2 or later, in which case the provisions + * of the GPL apply INSTEAD OF those given above. + * + * This software is provided ``as is'', and any express or implied + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose are disclaimed. + * In no event shall the company or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + * + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include +#include "stdint.h" + +/** Configuration of a instance of the dummy plugin */ +struct instance_config { + /** Observation Domain ID of generated messages */ + uint32_t odid; + /** Sleep time */ + struct timespec sleep_time; + + /** Number of messages to generate before failure */ + unsigned int fail_after; + /** Type of failure (i.e. return code) */ + int fail_type; +}; + +/** + * \brief Parse configuration of the plugin + * \param[in] ctx Instance context + * \param[in] params XML parameters + * \return Pointer to the parse configuration of the instance on success + * \return NULL if arguments are not valid or if a memory allocation error has occurred + */ +struct instance_config * +config_parse(ipx_ctx_t *ctx, const char *params); + +/** + * \brief Destroy parsed configuration + * \param[in] cfg Parsed configuration + */ +void +config_destroy(struct instance_config *cfg); + +#endif // CONFIG_H diff --git a/tests/modules/input_failure/dummy.c b/tests/modules/input_failure/dummy.c new file mode 100644 index 00000000..44bb5c09 --- /dev/null +++ b/tests/modules/input_failure/dummy.c @@ -0,0 +1,214 @@ +/** + * \file src/plugins/input/dummy/dummy.c + * \author Lukas Hutak + * \brief Example input plugin for IPFIXcol 2 (failure version) + * \date 2018-2020 + */ + +/* Copyright (C) 2018-2020 CESNET, z.s.p.o. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the Company nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * ALTERNATIVELY, provided that this notice is retained in full, this + * product may be distributed under the terms of the GNU General Public + * License (GPL) version 2 or later, in which case the provisions + * of the GPL apply INSTEAD OF those given above. + * + * This software is provided ``as is'', and any express or implied + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose are disclaimed. + * In no event shall the company or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + * + */ + +#include +#include +#include +#include "config.h" + +/** Plugin description */ +IPX_API struct ipx_plugin_info ipx_plugin_info = { + // Plugin type + .type = IPX_PT_INPUT, + // Plugin identification name + .name = "dummy-failure", + // Brief description of plugin + .dsc = "Example plugin that generates messages and fails.", + // Configuration flags (reserved for future use) + .flags = 0, + // Plugin version string (like "1.2.3") + .version = "2.0.0", + // Minimal IPFIXcol version string (like "1.2.3") + .ipx_min = "2.0.0" +}; + +/** Instance */ +struct instance_data { + /** Parsed configuration of the instance */ + struct instance_config *config; + /** Information about the source of flows */ + struct ipx_session *session; + + /** Failure status */ + bool fail_sent; + /** Remaining number of messages to generate */ + unsigned int fail_msg_remain; +}; + +/** + * \brief Sleep for specific time + * \param delay Delay specification + */ +static void +dummy_sleep(const struct timespec *delay) +{ + if (delay->tv_sec == 0 && delay->tv_nsec == 0) { + return; + } + + nanosleep(delay, NULL); +} + +int +ipx_plugin_init(ipx_ctx_t *ctx, const char *params) +{ + // Create a private data + struct instance_data *data = calloc(1, sizeof(*data)); + if (!data) { + return IPX_ERR_DENIED; + } + + // Parse configuration of the instance + if ((data->config = config_parse(ctx, params)) == NULL) { + free(data); + return IPX_ERR_DENIED; + } + + data->fail_msg_remain = data->config->fail_after; + data->fail_sent = false; + + // Store the instance data + ipx_ctx_private_set(ctx, data); + return IPX_OK; +} + +void +ipx_plugin_destroy(ipx_ctx_t *ctx, void *cfg) +{ + struct instance_data *data = (struct instance_data *) cfg; + + if (data->session != NULL) { + // Inform other plugins that the Transport Session is closed + ipx_msg_session_t *close_event = ipx_msg_session_create(data->session, IPX_MSG_SESSION_CLOSE); + ipx_ctx_msg_pass(ctx, ipx_msg_session2base(close_event)); + + /* The session cannot be freed because other plugin still have access to it. + * Send it as a garbage message after the Transport Session close event. + */ + ipx_msg_garbage_cb cb = (ipx_msg_garbage_cb) &ipx_session_destroy; + ipx_msg_garbage_t *garbage = ipx_msg_garbage_create(data->session, cb); + ipx_ctx_msg_pass(ctx, ipx_msg_garbage2base(garbage)); + } + + config_destroy(data->config); + free(data); +} + +int +ipx_plugin_get(ipx_ctx_t *ctx, void *cfg) +{ + struct instance_data *data = (struct instance_data *) cfg; + + if (data->fail_sent) { + IPX_CTX_ERROR(ctx, "ipx_plugin_get() was called again after termination request!", '\0'); + abort(); + } + + if (data->fail_msg_remain == 0) { + int code = data->config->fail_type; + IPX_CTX_WARNING(ctx, "Sending termination return code (%d)", code); + data->fail_sent = true; + return code; + } + data->fail_msg_remain--; + + if (data->session == NULL) { + // Create a info about a Transport Session (only once!) + struct ipx_session_net net_cfg; + net_cfg.l3_proto = AF_INET; + net_cfg.port_src = 0; + net_cfg.port_dst = 0; + if (inet_pton(AF_INET, "127.0.0.1", &net_cfg.addr_src.ipv4) != 1 + || inet_pton(AF_INET, "127.0.0.1", &net_cfg.addr_dst.ipv4) != 1) { + // inet_pton() failed! + IPX_CTX_ERROR(ctx, "inet_pton() failed!", '\0'); + return IPX_ERR_DENIED; + } + + data->session = ipx_session_new_tcp(&net_cfg); + if (!data->session) { + IPX_CTX_ERROR(ctx, "ipx_session_new_tcp() failed!", '\0'); + return IPX_ERR_DENIED; + } + + // Inform other plugins about the new Transport Session + ipx_msg_session_t *msg = ipx_msg_session_create(data->session, IPX_MSG_SESSION_OPEN); + ipx_ctx_msg_pass(ctx, ipx_msg_session2base(msg)); + } + + // Define a source of the a new IPFIX message + struct ipx_msg_ctx msg_ctx = { + .session = data->session, + .odid = data->config->odid, + .stream = 0 + }; + + // Create the empty IPFIX message + struct fds_ipfix_msg_hdr *ipfix_hdr = malloc(sizeof(*ipfix_hdr)); + if (!ipfix_hdr) { + // Allocation failed, but this is not a fatal error - just skip the message + IPX_CTX_ERROR(ctx, "Memory allocation failed! (%s:%d)", __FILE__, __LINE__); + dummy_sleep(&data->config->sleep_time); + return IPX_OK; + } + + ipfix_hdr->version = htons(FDS_IPFIX_VERSION); + ipfix_hdr->length = htons(FDS_IPFIX_MSG_HDR_LEN); + ipfix_hdr->export_time = htonl(time(NULL)); + ipfix_hdr->seq_num = htonl(0); + ipfix_hdr->odid = htonl(data->config->odid); + + // Insert the message and info about source into a wrapper + ipx_msg_ipfix_t *msg2send = ipx_msg_ipfix_create(ctx, &msg_ctx, (uint8_t *) ipfix_hdr, + FDS_IPFIX_MSG_HDR_LEN); + if (!msg2send) { + // Allocation failed, but this is not a fatal error - just skip the message + IPX_CTX_ERROR(ctx, "Memory allocation failed! (%s:%d)", __FILE__, __LINE__); + dummy_sleep(&data->config->sleep_time); + return IPX_OK; + } + + // Pass the message + ipx_ctx_msg_pass(ctx, ipx_msg_ipfix2base(msg2send)); + + dummy_sleep(&data->config->sleep_time); + return IPX_OK; +} \ No newline at end of file diff --git a/tests/modules/intermediate_failure/CMakeLists.txt b/tests/modules/intermediate_failure/CMakeLists.txt new file mode 100644 index 00000000..c2d34c6b --- /dev/null +++ b/tests/modules/intermediate_failure/CMakeLists.txt @@ -0,0 +1,6 @@ +# Create a linkable module +add_library(dummy-failure-intermediate MODULE + dummy.c + config.c + config.h +) diff --git a/tests/modules/intermediate_failure/config.c b/tests/modules/intermediate_failure/config.c new file mode 100644 index 00000000..00f6c9c9 --- /dev/null +++ b/tests/modules/intermediate_failure/config.c @@ -0,0 +1,178 @@ +/** + * \file src/plugins/output/dummy/config.c + * \author Lukas Hutak + * \brief Example parser of an XML configuration (source file) + * \date 2018 + */ + +/* Copyright (C) 2018 CESNET, z.s.p.o. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the Company nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * ALTERNATIVELY, provided that this notice is retained in full, this + * product may be distributed under the terms of the GNU General Public + * License (GPL) version 2 or later, in which case the provisions + * of the GPL apply INSTEAD OF those given above. + * + * This software is provided ``as is'', and any express or implied + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose are disclaimed. + * In no event shall the company or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + * + */ + +#include +#include +#include "config.h" + +/* + * + * ... + * ... + * ... + * + */ + +/** XML nodes */ +enum params_xml_nodes { + NODE_DELAY = 1, + NODE_FAIL_AFTER, + NODE_FAIL_TYPE +}; + +/** Definition of the \ node */ +static const struct fds_xml_args args_params[] = { + FDS_OPTS_ROOT("params"), + FDS_OPTS_ELEM(NODE_DELAY, "delay", FDS_OPTS_T_UINT, 0), + FDS_OPTS_ELEM(NODE_FAIL_AFTER, "failAfter", FDS_OPTS_T_UINT, 0), + FDS_OPTS_ELEM(NODE_FAIL_TYPE, "failType", FDS_OPTS_T_STRING, 0), + FDS_OPTS_END +}; + +/** + * \brief Process \ node + * \param[in] ctx Plugin context + * \param[in] root XML context to process + * \param[in] cfg Parsed configuration + * \return #IPX_OK on success + * \return #IPX_ERR_FORMAT in case of failure + */ +static int +config_parser_root(ipx_ctx_t *ctx, fds_xml_ctx_t *root, struct instance_config *cfg) +{ + (void) ctx; + + const struct fds_xml_cont *content; + while(fds_xml_next(root, &content) != FDS_EOC) { + switch (content->id) { + case NODE_DELAY: + // Delay between messages [microseconds] + assert(content->type == FDS_OPTS_T_UINT); + cfg->sleep_time.tv_nsec = (content->val_uint % 1000000LL) * 1000LL; + cfg->sleep_time.tv_sec = content->val_uint / 1000000LL; + break; + case NODE_FAIL_AFTER: + // Fail after generating specific number of messages + assert(content->type == FDS_OPTS_T_UINT); + cfg->fail_after = content->val_uint; + break; + case NODE_FAIL_TYPE: + assert(content->type == FDS_OPTS_T_STRING); + if (strcasecmp(content->ptr_string, "IPX_ERR_DENIED") == 0) { + cfg->fail_type = IPX_ERR_DENIED; + } else if (strcasecmp(content->ptr_string, "IPX_ERR_EOF") == 0) { + cfg->fail_type = IPX_ERR_EOF; + } else { + IPX_CTX_ERROR(ctx, "Invalid failure type (expected: IPX_ERR_EOF/IPX_ERR_DENIED", '\0'); + return IPX_ERR_FORMAT; + } + break; + default: + // Internal error + assert(false); + } + } + + return IPX_OK; +} + +/** + * \brief Set default parameters of the configuration + * \param[in] cfg Configuration + */ +static void +config_default_set(struct instance_config *cfg) +{ + cfg->sleep_time.tv_sec = 0; + cfg->sleep_time.tv_nsec = 100000000LL; // 100ms between messages +} + +struct instance_config * +config_parse(ipx_ctx_t *ctx, const char *params) +{ + struct instance_config *cfg = calloc(1, sizeof(*cfg)); + if (!cfg) { + IPX_CTX_ERROR(ctx, "Memory allocation error (%s:%d)", __FILE__, __LINE__); + return NULL; + } + + // Set default parameters + config_default_set(cfg); + + // Create an XML parser + fds_xml_t *parser = fds_xml_create(); + if (!parser) { + IPX_CTX_ERROR(ctx, "Memory allocation error (%s:%d)", __FILE__, __LINE__); + free(cfg); + return NULL; + } + + if (fds_xml_set_args(parser, args_params) != FDS_OK) { + IPX_CTX_ERROR(ctx, "Failed to parse the description of an XML document!", '\0'); + fds_xml_destroy(parser); + free(cfg); + return NULL; + } + + fds_xml_ctx_t *params_ctx = fds_xml_parse_mem(parser, params, true); + if (params_ctx == NULL) { + IPX_CTX_ERROR(ctx, "Failed to parse the configuration: %s", fds_xml_last_err(parser)); + fds_xml_destroy(parser); + free(cfg); + return NULL; + } + + // Parse parameters + int rc = config_parser_root(ctx, params_ctx, cfg); + fds_xml_destroy(parser); + if (rc != IPX_OK) { + free(cfg); + return NULL; + } + + return cfg; +} + +void +config_destroy(struct instance_config *cfg) +{ + free(cfg); +} diff --git a/tests/modules/intermediate_failure/config.h b/tests/modules/intermediate_failure/config.h new file mode 100644 index 00000000..0fd2b367 --- /dev/null +++ b/tests/modules/intermediate_failure/config.h @@ -0,0 +1,76 @@ +/** + * \file src/plugins/output/dummy/config.h + * \author Lukas Hutak + * \brief Example parser of an XML configuration (header file) + * \date 2018 + */ + +/* Copyright (C) 2018 CESNET, z.s.p.o. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the Company nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * ALTERNATIVELY, provided that this notice is retained in full, this + * product may be distributed under the terms of the GNU General Public + * License (GPL) version 2 or later, in which case the provisions + * of the GPL apply INSTEAD OF those given above. + * + * This software is provided ``as is'', and any express or implied + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose are disclaimed. + * In no event shall the company or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + * + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include +#include "stdint.h" + +/** Configuration of a instance of the dummy plugin */ +struct instance_config { + /** Sleep time */ + struct timespec sleep_time; + + /** Number of messages to generate before failure */ + unsigned int fail_after; + /** Type of failure (i.e. return code) */ + int fail_type; +}; + +/** + * \brief Parse configuration of the plugin + * \param[in] ctx Instance context + * \param[in] params XML parameters + * \return Pointer to the parse configuration of the instance on success + * \return NULL if arguments are not valid or if a memory allocation error has occurred + */ +struct instance_config * +config_parse(ipx_ctx_t *ctx, const char *params); + +/** + * \brief Destroy parsed configuration + * \param[in] cfg Parsed configuration + */ +void +config_destroy(struct instance_config *cfg); + +#endif // CONFIG_H diff --git a/tests/modules/intermediate_failure/dummy.c b/tests/modules/intermediate_failure/dummy.c new file mode 100644 index 00000000..9361455f --- /dev/null +++ b/tests/modules/intermediate_failure/dummy.c @@ -0,0 +1,154 @@ +/** + * \file src/plugins/output/dummy/dummy.c + * \author Lukas Hutak + * \brief Example intermediate plugin for IPFIXcol 2 (failure version) + * \date 2018-2020 + */ + +/* Copyright (C) 2018-2020 CESNET, z.s.p.o. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the Company nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * ALTERNATIVELY, provided that this notice is retained in full, this + * product may be distributed under the terms of the GNU General Public + * License (GPL) version 2 or later, in which case the provisions + * of the GPL apply INSTEAD OF those given above. + * + * This software is provided ``as is'', and any express or implied + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose are disclaimed. + * In no event shall the company or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + * + */ + +#include +#include +#include +#include + +#include "config.h" + +/** Plugin description */ +IPX_API struct ipx_plugin_info ipx_plugin_info = { + // Plugin type + .type = IPX_PT_INTERMEDIATE, + // Plugin identification name + .name = "dummy-failure", + // Brief description of plugin + .dsc = "Example intermediate plugin that fails.", + // Configuration flags (reserved for future use) + .flags = 0, + // Plugin version string (like "1.2.3") + .version = "2.0.0", + // Minimal IPFIXcol version string (like "1.2.3") + .ipx_min = "2.0.0" +}; + +/** Instance */ +struct instance_data { + /** Parsed configuration of the instance */ + struct instance_config *config; + + /** Failure status */ + bool fail_sent; + /** Remaining number of messages to generate */ + unsigned int fail_msg_remain; +}; + +int +ipx_plugin_init(ipx_ctx_t *ctx, const char *params) +{ + // Create a private data + struct instance_data *data = calloc(1, sizeof(*data)); + if (!data) { + return IPX_ERR_DENIED; + } + + if ((data->config = config_parse(ctx, params)) == NULL) { + free(data); + return IPX_ERR_DENIED; + } + + data->fail_msg_remain = data->config->fail_after; + data->fail_sent = false; + ipx_ctx_private_set(ctx, data); + + // Subscribe to receive IPFIX messages and Transport Session events + uint16_t new_mask = IPX_MSG_IPFIX | IPX_MSG_SESSION; + ipx_ctx_subscribe(ctx, &new_mask, NULL); + return IPX_OK; +} + +void +ipx_plugin_destroy(ipx_ctx_t *ctx, void *cfg) +{ + (void) ctx; // Suppress warnings + + struct instance_data *data = (struct instance_data *) cfg; + config_destroy(data->config); + free(data); +} + +int +ipx_plugin_process(ipx_ctx_t *ctx, void *cfg, ipx_msg_t *msg) +{ + struct instance_data *data = (struct instance_data *) cfg; + + if (data->fail_sent) { + IPX_CTX_ERROR(ctx, "ipx_plugin_process() was called again after termination request!", '\0'); + abort(); + } + + if (data->fail_msg_remain == 0) { + int code = data->config->fail_type; + data->fail_sent = true; + ipx_msg_destroy(msg); + + IPX_CTX_WARNING(ctx, "Sending termination return code (%d)", code); + return code; + } + data->fail_msg_remain--; + + int type = ipx_msg_get_type(msg); + if (type == IPX_MSG_IPFIX) { + // Process IPFIX message + ipx_msg_ipfix_t *ipfix_msg = ipx_msg_base2ipfix(msg); + const struct ipx_msg_ctx *ipfix_ctx = ipx_msg_ipfix_get_ctx(ipfix_msg); + IPX_CTX_INFO(ctx, "[ODID: %" PRIu32 "] Received an IPFIX message", ipfix_ctx->odid); + } + + if (type == IPX_MSG_SESSION) { + // Process Transport Session Event + ipx_msg_session_t *session_msg = ipx_msg_base2session(msg); + enum ipx_msg_session_event event = ipx_msg_session_get_event(session_msg); + const struct ipx_session *session = ipx_msg_session_get_session(session_msg); + const char *status_msg = (event == IPX_MSG_SESSION_OPEN) ? "opened" : "closed"; + IPX_CTX_INFO(ctx, "Transport Session '%s' %s", session->ident, status_msg); + } + + const struct timespec *delay = &data->config->sleep_time; + if (delay->tv_sec != 0 || delay->tv_nsec != 0) { + nanosleep(delay, NULL); + } + + ipx_ctx_msg_pass(ctx, msg); + return IPX_OK; +} diff --git a/tests/modules/output_failure/CMakeLists.txt b/tests/modules/output_failure/CMakeLists.txt new file mode 100644 index 00000000..dbe9a21e --- /dev/null +++ b/tests/modules/output_failure/CMakeLists.txt @@ -0,0 +1,6 @@ +# Create a linkable module +add_library(dummy-failure-output MODULE + dummy.c + config.c + config.h +) diff --git a/tests/modules/output_failure/config.c b/tests/modules/output_failure/config.c new file mode 100644 index 00000000..00f6c9c9 --- /dev/null +++ b/tests/modules/output_failure/config.c @@ -0,0 +1,178 @@ +/** + * \file src/plugins/output/dummy/config.c + * \author Lukas Hutak + * \brief Example parser of an XML configuration (source file) + * \date 2018 + */ + +/* Copyright (C) 2018 CESNET, z.s.p.o. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the Company nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * ALTERNATIVELY, provided that this notice is retained in full, this + * product may be distributed under the terms of the GNU General Public + * License (GPL) version 2 or later, in which case the provisions + * of the GPL apply INSTEAD OF those given above. + * + * This software is provided ``as is'', and any express or implied + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose are disclaimed. + * In no event shall the company or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + * + */ + +#include +#include +#include "config.h" + +/* + * + * ... + * ... + * ... + * + */ + +/** XML nodes */ +enum params_xml_nodes { + NODE_DELAY = 1, + NODE_FAIL_AFTER, + NODE_FAIL_TYPE +}; + +/** Definition of the \ node */ +static const struct fds_xml_args args_params[] = { + FDS_OPTS_ROOT("params"), + FDS_OPTS_ELEM(NODE_DELAY, "delay", FDS_OPTS_T_UINT, 0), + FDS_OPTS_ELEM(NODE_FAIL_AFTER, "failAfter", FDS_OPTS_T_UINT, 0), + FDS_OPTS_ELEM(NODE_FAIL_TYPE, "failType", FDS_OPTS_T_STRING, 0), + FDS_OPTS_END +}; + +/** + * \brief Process \ node + * \param[in] ctx Plugin context + * \param[in] root XML context to process + * \param[in] cfg Parsed configuration + * \return #IPX_OK on success + * \return #IPX_ERR_FORMAT in case of failure + */ +static int +config_parser_root(ipx_ctx_t *ctx, fds_xml_ctx_t *root, struct instance_config *cfg) +{ + (void) ctx; + + const struct fds_xml_cont *content; + while(fds_xml_next(root, &content) != FDS_EOC) { + switch (content->id) { + case NODE_DELAY: + // Delay between messages [microseconds] + assert(content->type == FDS_OPTS_T_UINT); + cfg->sleep_time.tv_nsec = (content->val_uint % 1000000LL) * 1000LL; + cfg->sleep_time.tv_sec = content->val_uint / 1000000LL; + break; + case NODE_FAIL_AFTER: + // Fail after generating specific number of messages + assert(content->type == FDS_OPTS_T_UINT); + cfg->fail_after = content->val_uint; + break; + case NODE_FAIL_TYPE: + assert(content->type == FDS_OPTS_T_STRING); + if (strcasecmp(content->ptr_string, "IPX_ERR_DENIED") == 0) { + cfg->fail_type = IPX_ERR_DENIED; + } else if (strcasecmp(content->ptr_string, "IPX_ERR_EOF") == 0) { + cfg->fail_type = IPX_ERR_EOF; + } else { + IPX_CTX_ERROR(ctx, "Invalid failure type (expected: IPX_ERR_EOF/IPX_ERR_DENIED", '\0'); + return IPX_ERR_FORMAT; + } + break; + default: + // Internal error + assert(false); + } + } + + return IPX_OK; +} + +/** + * \brief Set default parameters of the configuration + * \param[in] cfg Configuration + */ +static void +config_default_set(struct instance_config *cfg) +{ + cfg->sleep_time.tv_sec = 0; + cfg->sleep_time.tv_nsec = 100000000LL; // 100ms between messages +} + +struct instance_config * +config_parse(ipx_ctx_t *ctx, const char *params) +{ + struct instance_config *cfg = calloc(1, sizeof(*cfg)); + if (!cfg) { + IPX_CTX_ERROR(ctx, "Memory allocation error (%s:%d)", __FILE__, __LINE__); + return NULL; + } + + // Set default parameters + config_default_set(cfg); + + // Create an XML parser + fds_xml_t *parser = fds_xml_create(); + if (!parser) { + IPX_CTX_ERROR(ctx, "Memory allocation error (%s:%d)", __FILE__, __LINE__); + free(cfg); + return NULL; + } + + if (fds_xml_set_args(parser, args_params) != FDS_OK) { + IPX_CTX_ERROR(ctx, "Failed to parse the description of an XML document!", '\0'); + fds_xml_destroy(parser); + free(cfg); + return NULL; + } + + fds_xml_ctx_t *params_ctx = fds_xml_parse_mem(parser, params, true); + if (params_ctx == NULL) { + IPX_CTX_ERROR(ctx, "Failed to parse the configuration: %s", fds_xml_last_err(parser)); + fds_xml_destroy(parser); + free(cfg); + return NULL; + } + + // Parse parameters + int rc = config_parser_root(ctx, params_ctx, cfg); + fds_xml_destroy(parser); + if (rc != IPX_OK) { + free(cfg); + return NULL; + } + + return cfg; +} + +void +config_destroy(struct instance_config *cfg) +{ + free(cfg); +} diff --git a/tests/modules/output_failure/config.h b/tests/modules/output_failure/config.h new file mode 100644 index 00000000..0fd2b367 --- /dev/null +++ b/tests/modules/output_failure/config.h @@ -0,0 +1,76 @@ +/** + * \file src/plugins/output/dummy/config.h + * \author Lukas Hutak + * \brief Example parser of an XML configuration (header file) + * \date 2018 + */ + +/* Copyright (C) 2018 CESNET, z.s.p.o. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the Company nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * ALTERNATIVELY, provided that this notice is retained in full, this + * product may be distributed under the terms of the GNU General Public + * License (GPL) version 2 or later, in which case the provisions + * of the GPL apply INSTEAD OF those given above. + * + * This software is provided ``as is'', and any express or implied + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose are disclaimed. + * In no event shall the company or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + * + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include +#include "stdint.h" + +/** Configuration of a instance of the dummy plugin */ +struct instance_config { + /** Sleep time */ + struct timespec sleep_time; + + /** Number of messages to generate before failure */ + unsigned int fail_after; + /** Type of failure (i.e. return code) */ + int fail_type; +}; + +/** + * \brief Parse configuration of the plugin + * \param[in] ctx Instance context + * \param[in] params XML parameters + * \return Pointer to the parse configuration of the instance on success + * \return NULL if arguments are not valid or if a memory allocation error has occurred + */ +struct instance_config * +config_parse(ipx_ctx_t *ctx, const char *params); + +/** + * \brief Destroy parsed configuration + * \param[in] cfg Parsed configuration + */ +void +config_destroy(struct instance_config *cfg); + +#endif // CONFIG_H diff --git a/tests/modules/output_failure/dummy.c b/tests/modules/output_failure/dummy.c new file mode 100644 index 00000000..7bf52e79 --- /dev/null +++ b/tests/modules/output_failure/dummy.c @@ -0,0 +1,151 @@ +/** + * \file src/plugins/output/dummy/dummy.c + * \author Lukas Hutak + * \brief Example output plugin for IPFIXcol 2 (failure version) + * \date 2018-2020 + */ + +/* Copyright (C) 2018-2020 CESNET, z.s.p.o. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of the Company nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * ALTERNATIVELY, provided that this notice is retained in full, this + * product may be distributed under the terms of the GNU General Public + * License (GPL) version 2 or later, in which case the provisions + * of the GPL apply INSTEAD OF those given above. + * + * This software is provided ``as is'', and any express or implied + * warranties, including, but not limited to, the implied warranties of + * merchantability and fitness for a particular purpose are disclaimed. + * In no event shall the company or contributors be liable for any + * direct, indirect, incidental, special, exemplary, or consequential + * damages (including, but not limited to, procurement of substitute + * goods or services; loss of use, data, or profits; or business + * interruption) however caused and on any theory of liability, whether + * in contract, strict liability, or tort (including negligence or + * otherwise) arising in any way out of the use of this software, even + * if advised of the possibility of such damage. + * + */ + +#include +#include +#include +#include + +#include "config.h" + +/** Plugin description */ +IPX_API struct ipx_plugin_info ipx_plugin_info = { + // Plugin type + .type = IPX_PT_OUTPUT, + // Plugin identification name + .name = "dummy-failure", + // Brief description of plugin + .dsc = "Example output plugin that fails.", + // Configuration flags (reserved for future use) + .flags = 0, + // Plugin version string (like "1.2.3") + .version = "2.0.0", + // Minimal IPFIXcol version string (like "1.2.3") + .ipx_min = "2.0.0" +}; + +/** Instance */ +struct instance_data { + /** Parsed configuration of the instance */ + struct instance_config *config; + + /** Failure status */ + bool fail_sent; + /** Remaining number of messages to generate */ + unsigned int fail_msg_remain; +}; + +int +ipx_plugin_init(ipx_ctx_t *ctx, const char *params) +{ + // Create a private data + struct instance_data *data = calloc(1, sizeof(*data)); + if (!data) { + return IPX_ERR_DENIED; + } + + if ((data->config = config_parse(ctx, params)) == NULL) { + free(data); + return IPX_ERR_DENIED; + } + + data->fail_msg_remain = data->config->fail_after; + data->fail_sent = false; + ipx_ctx_private_set(ctx, data); + + // Subscribe to receive IPFIX messages and Transport Session events + uint16_t new_mask = IPX_MSG_IPFIX | IPX_MSG_SESSION; + ipx_ctx_subscribe(ctx, &new_mask, NULL); + return IPX_OK; +} + +void +ipx_plugin_destroy(ipx_ctx_t *ctx, void *cfg) +{ + (void) ctx; // Suppress warnings + + struct instance_data *data = (struct instance_data *) cfg; + config_destroy(data->config); + free(data); +} + +int +ipx_plugin_process(ipx_ctx_t *ctx, void *cfg, ipx_msg_t *msg) +{ + struct instance_data *data = (struct instance_data *) cfg; + + if (data->fail_sent) { + IPX_CTX_ERROR(ctx, "ipx_plugin_process() was called again after termination request!", '\0'); + abort(); + } + + if (data->fail_msg_remain == 0) { + int code = data->config->fail_type; + IPX_CTX_WARNING(ctx, "Sending termination return code (%d)", code); + data->fail_sent = true; + return code; + } + data->fail_msg_remain--; + + int type = ipx_msg_get_type(msg); + if (type == IPX_MSG_IPFIX) { + // Process IPFIX message + ipx_msg_ipfix_t *ipfix_msg = ipx_msg_base2ipfix(msg); + const struct ipx_msg_ctx *ipfix_ctx = ipx_msg_ipfix_get_ctx(ipfix_msg); + IPX_CTX_INFO(ctx, "[ODID: %" PRIu32 "] Received an IPFIX message", ipfix_ctx->odid); + } + + if (type == IPX_MSG_SESSION) { + // Process Transport Session Event + ipx_msg_session_t *session_msg = ipx_msg_base2session(msg); + enum ipx_msg_session_event event = ipx_msg_session_get_event(session_msg); + const struct ipx_session *session = ipx_msg_session_get_session(session_msg); + const char *status_msg = (event == IPX_MSG_SESSION_OPEN) ? "opened" : "closed"; + IPX_CTX_INFO(ctx, "Transport Session '%s' %s", session->ident, status_msg); + } + + const struct timespec *delay = &data->config->sleep_time; + if (delay->tv_sec != 0 || delay->tv_nsec != 0) { + nanosleep(delay, NULL); + } + + return IPX_OK; +}