diff options
| author | songyanchao <[email protected]> | 2024-07-31 07:38:57 +0000 |
|---|---|---|
| committer | songyanchao <[email protected]> | 2024-08-09 09:31:48 +0000 |
| commit | 7aed37ad50e7e1da4d56ccd5642b0bb823f764e1 (patch) | |
| tree | 3df7db26ec6c73c085900c46f05ea41d05150091 | |
| parent | 15c83d74fed6280613dfa6856832dff3bcc31077 (diff) | |
🧪 test(DPISDN-52): Add CMocka-based test cases for BFD node.
Add CMocka-based test cases for BFD node.
| -rw-r--r-- | infra/include/common.h | 31 | ||||
| -rw-r--r-- | service/CMakeLists.txt | 17 | ||||
| -rw-r--r-- | service/include/sc_metrics.h | 82 | ||||
| -rw-r--r-- | service/src/core.c | 10 | ||||
| -rw-r--r-- | service/src/node_bfd.c | 876 | ||||
| -rw-r--r-- | service/src/sc_metrics.c | 221 | ||||
| -rw-r--r-- | service/test/test_common.c | 167 | ||||
| -rw-r--r-- | service/test/test_node_bfd.c | 970 | ||||
| -rw-r--r-- | service/test/test_sc_metrics.c | 128 | ||||
| -rw-r--r-- | test/ptf_test/CMakeLists.txt | 2 |
10 files changed, 2266 insertions, 238 deletions
diff --git a/infra/include/common.h b/infra/include/common.h index 881ee2d..f3fd99d 100644 --- a/infra/include/common.h +++ b/infra/include/common.h @@ -376,6 +376,37 @@ struct g_vxlan_hdr #define G_VXLAN_INNER_LINK_TYPE_PPP (0x8) #define G_VXLAN_INNER_LINK_TYPE_HDLC (0xC) +/* =============================== BFD_DEFINE =============================== */ + +struct bfd_hdr +{ + uint8_t version_diag; +#if __BYTE_ORDER == __LITTLE_ENDIAN + uint8_t flags : 6; + uint8_t state : 2; +#elif __BYTE_ORDER == __BIG_ENDIAN + uint8_t state : 2; + uint8_t flags : 6; +#endif + + uint8_t detect_time_multiplier; + uint8_t length; + uint8_t my_discriminator[4]; + uint8_t your_discriminator[4]; + uint8_t desired_min_tx_interval[4]; + uint8_t required_min_rx_interval[4]; + uint8_t required_min_echo_interval[4]; +}; + +/* Bfd State */ +enum +{ + BFD_STATE_ADMINDOWN = 0, + BFD_STATE_DOWN, + BFD_STATE_INIT, + BFD_STATE_UP +}; + static inline int mr_thread_setname(pthread_t id, const char * name) { // name length less 16 characters diff --git a/service/CMakeLists.txt b/service/CMakeLists.txt index e4d9d04..d33d97d 100644 --- a/service/CMakeLists.txt +++ b/service/CMakeLists.txt @@ -2,7 +2,7 @@ include_directories(include) set(MRZCPD_SERVICE_SOURCE_FILES src/core.c src/devmgr.c src/mrb.c src/hwinfo.c src/vdev.c - src/vdata.c src/app.c src/monit.c src/node.c src/node_shmdev.c + src/vdata.c src/app.c src/monit.c src/node.c src/sc_metrics.c src/node_shmdev.c src/node_phydev.c src/node_lb.c src/node_classifier.c src/node_etherfabric.c src/node_tera.c src/node_eth_ingress.c src/node_eth_egress.c src/node_bfd.c src/node_vwire.c src/node_health_check.c src/node_forwarder.c src/node_bridge.c src/pdump.c src/olp.c src/olp_6500.c @@ -44,3 +44,18 @@ target_link_libraries(test_trace_service MESA_prof_load_static libcmocka rt pthr target_link_options(test_trace_service PRIVATE -Wl,--wrap=rte_mp_reply,--wrap=app_event_handler_register,--wrap=app_lookup_by_symbol) target_link_options(test_trace_service PRIVATE -Wl,--wrap=sc_main_get) add_test(NAME test_trace_service COMMAND test_trace_service) + +add_executable(test_node_bfd test/test_node_mocks.c test/test_node_bfd.c test/test_common.c ${MRZCPD_SERVICE_SOURCE_FILES}) +target_compile_options(test_node_bfd PRIVATE --coverage -fprofile-arcs -ftest-coverage) +target_link_libraries(test_node_bfd MESA_prof_load_static ${SYSTEMD_LIBRARIES} libdpdk) +target_link_libraries(test_node_bfd libcmocka rt pthread dl infra z elf) +target_link_options(test_node_bfd PRIVATE --coverage -fprofile-arcs -ftest-coverage) +target_include_directories(test_node_bfd PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include/") + + +add_executable(test_sc_metrics test/test_node_mocks.c test/test_sc_metrics.c ${MRZCPD_SERVICE_SOURCE_FILES}) +target_compile_options(test_sc_metrics PRIVATE --coverage -fprofile-arcs -ftest-coverage) +target_link_libraries(test_sc_metrics MESA_prof_load_static ${SYSTEMD_LIBRARIES} libdpdk) +target_link_libraries(test_sc_metrics libcmocka rt pthread dl infra z elf) +target_link_options(test_sc_metrics PRIVATE --coverage -fprofile-arcs -ftest-coverage) +target_include_directories(test_sc_metrics PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include/") diff --git a/service/include/sc_metrics.h b/service/include/sc_metrics.h new file mode 100644 index 0000000..4260564 --- /dev/null +++ b/service/include/sc_metrics.h @@ -0,0 +1,82 @@ +/** + * @file sc_metrics.h + * @brief Header file for SC metrics handle management. + * + * This header file defines the data structures and function prototypes for managing + * SC metrics handles in the system. The SC metrics handle is used to store and process + * metric values, with functions provided for creating, deleting, and manipulating + * these handles. Key operations include setting, resetting, and accumulating metric values. + * + * The following data structures and functions are defined: + * + * Data Structures: + * - `struct sc_metrics_handle`: Represents a metrics handle with a capacity and a pointer + * to an array of metric values. + * + * Functions: + * - `sc_metrics_handle_create(uint16_t capacity)`: Allocates and initializes a new SC metrics + * handle with the specified capacity, which is rounded up to the nearest multiple of 8. + * + * - `sc_metrics_handle_delete(struct sc_metrics_handle * metrics_handle)`: Deallocates the memory + * associated with an SC metrics handle, including the values array. + * + * - `sc_metrics_values_set(struct sc_metrics_handle * metrics_handle, uint64_t values[], uint16_t capacity)`: + * Sets the metric values in the handle. The provided capacity must not exceed the handle's allocated + * capacity. + * + * - `sc_metrics_values_reset(struct sc_metrics_handle * metrics_handle)`: Resets all metric values + * in the handle to zero. + * + * - `sc_metrics_accumulate(struct sc_metrics_handle * metrics_handle, uint64_t inc_values[], uint16_t capacity)`: + * Accumulates new values into the existing metric values within the handle. The provided capacity + * must not exceed the handle's allocated capacity. + */ + +#pragma once + +#include <common.h> + +#define SC_METRICS_MAX_CAPACITY 32 + +struct sc_metrics_handle +{ + uint16_t capacity; + volatile uint64_t * values; +}; + +struct sc_metrics_handle * sc_metrics_handle_create(uint16_t capacity); +int sc_metrics_handle_delete(struct sc_metrics_handle * metrics_handle); +int sc_metrics_value_get(struct sc_metrics_handle * metrics_handle, uint64_t * value, uint16_t key); +int sc_metrics_values_get(struct sc_metrics_handle * metrics_handle, uint64_t values[], uint16_t capacity); +static inline int sc_metrics_value_set(struct sc_metrics_handle * metrics_handle, uint64_t value, uint16_t key); +int sc_metrics_values_set(struct sc_metrics_handle * metrics_handle, uint64_t values[], uint16_t capacity); +int sc_metrics_values_reset(struct sc_metrics_handle * metrics_handle); +int sc_metrics_accumulate(struct sc_metrics_handle * metrics_handle, uint64_t inc_values[], uint16_t capacity); + +/** + * @brief Set a single value for the SC metrics handle. + * + * This function sets a single metric value in the SC metrics handle at the + * specified key. The function ensures that the key is within the handle's + * allocated capacity. The value is directly written to the handle's values + * array at the specified key. + * + * @param metrics_handle Pointer to the SC metrics handle where the value will + * be set. This handle must be valid and initialized. + * @param value The metric value to set in the handle. + * @param key The key at which to set the metric value. This key must be within + * the handle's allocated capacity. + * + * @return Return value indicating success: + * - RT_SUCCESS (0) if the value is successfully set. + */ +static inline int sc_metrics_value_set(struct sc_metrics_handle * metrics_handle, uint64_t value, uint16_t key) +{ + assert(metrics_handle != NULL); + assert(metrics_handle->values != NULL); + assert(key < metrics_handle->capacity); + + metrics_handle->values[key] = value; + + return RT_SUCCESS; +} diff --git a/service/src/core.c b/service/src/core.c index d79a176..111a3b4 100644 --- a/service/src/core.c +++ b/service/src/core.c @@ -932,6 +932,7 @@ extern int mr_pdump_init(struct sc_main * sc); extern int http_serv_init(struct sc_main * sc_main); extern int olp_manager_init(struct sc_main * sc_main); extern int lai_init(struct sc_main * sc); +extern int bfd_init(struct sc_main * sc); int marsio_service_main(int argc, char * argv[]) { @@ -1193,7 +1194,7 @@ int marsio_service_main(int argc, char * argv[]) if (mr_pdump_init(sc) != RT_SUCCESS) { - MR_ERROR("mrpdump initialization failed. "); + MR_ERROR("Mrpdump initialization failed. "); ret = EXIT_FAILURE; goto quit; } @@ -1205,6 +1206,13 @@ int marsio_service_main(int argc, char * argv[]) goto quit; } + if (bfd_init(sc) != RT_SUCCESS) + { + MR_ERROR("BFD initialization failed. "); + ret = EXIT_FAILURE; + goto quit; + } + sc_config_dump(sc); vdev_dump(sc); port_adapter_mapping_dump(); diff --git a/service/src/node_bfd.c b/service/src/node_bfd.c index 5a8e812..aa0fa9c 100644 --- a/service/src/node_bfd.c +++ b/service/src/node_bfd.c @@ -1,18 +1,32 @@ -#include "common.h" #include <cJSON.h> + +#include <rte_branch_prediction.h> #include <rte_graph.h> #include <rte_graph_worker.h> +#include <MESA_prof_load.h> +#include <common.h> #include <sc_common.h> +#include <sc_metrics.h> #include <sc_node_common.h> #include <sc_trace.h> -#include <stdint.h> -/* Global Config */ -#define MR_BFD_MAX_SESSION_NUM 128 -#define MR_BFD_START_DISCRIMINATOR 1000 +/* Global bfd configuration */ +#define MR_BFD_MAX_SESSION_NUM 128 // Maximum number of BFD sessions +#define MR_BFD_START_DISCRIMINATOR 1000 // Starting value for BFD discriminators -/* Bfd Next Node */ +/*************************************************** Bfd node main ****************************************************/ +/* Bfd node metrics */ +enum bfd_node_metrics_key +{ + BFD_METRIC_TOT_PKTS = 0, // Total packets + BFD_METRIC_PKTS_BATCH, // Packets per batch + BFD_METRIC_DROP_UNEXP_PKTS, // Dropped unexpected packets + BFD_METRIC_DROP_NO_FREE_SESS_PKTS, // Dropped packets due to no free session ID + BFD_METRIC_MAX // Maximum metrics count +}; + +/* Bfd next node */ enum { BFD_NEXT_PKT_DROP = 0, @@ -20,91 +34,466 @@ enum BFD_NEXT_MAX }; -/* Bfd State */ -enum +/* BFD discriminator processing method */ +enum bfd_discriminator_method { - BFD_STATE_ADMINDOWN = 0, - BFD_STATE_DOWN, - BFD_STATE_INIT, - BFD_STATE_UP + BFD_DISCRIMINATOR_METHOD_RFC_STANDARD = 0, // Based on RFC standard + BFD_DISCRIMINATOR_METHOD_FLIPPING, // Flipping the discriminator + BFD_DISCRIMINATOR_METHOD_MAX }; -struct bfd_header_t +/* Bfd node main struct */ +struct bfd_node_main { - uint8_t version_diag; -#if __BYTE_ORDER == __LITTLE_ENDIAN - uint8_t flags : 6; - uint8_t state : 2; -#elif __BYTE_ORDER == __BIG_ENDIAN - uint8_t state : 2; - uint8_t flags : 6; -#endif - - uint8_t detect_time_multiplier; - uint8_t length; - uint8_t my_discriminator[4]; - uint8_t your_discriminator[4]; - uint8_t desired_min_tx_interval[4]; - uint8_t required_min_rx_interval[4]; - uint8_t required_min_echo_interval[4]; + /* Number of graphs */ + uint16_t nr_graphs; + + /* Discriminator processing method */ + enum bfd_discriminator_method discriminator_method; + + /* Bfd session handles */ + struct bfd_session_handle ** bfd_session_handles; + + /* Bfd metrics handles */ + struct sc_metrics_handle ** metrics_handles; }; -/* Bfd Stat Field */ -struct bfd_stats_for_session +/* Global variable for BFD node management */ +static struct bfd_node_main * g_bfd_main = NULL; // Pointer to the main BFD node structure + +extern struct bfd_session_handle * bfd_session_handle_create(uint16_t max_sessions); +int bfd_session_handle_delete(struct bfd_session_handle * bfd_session_handle); + +/** + * @brief Delete the BFD node main struct. + * + * This function frees the memory allocated for the BFD node main struct. + * Note: This function is not thread-safe. + * + * @param bfd_node_main Pointer to the BFD node main struct to delete. + */ +int bfd_node_main_delete(struct bfd_node_main * bfd_node_main) +{ + if (bfd_node_main == NULL) + { + return RT_ERR; + } + + for (uint16_t i = 0; i < bfd_node_main->nr_graphs; i++) + { + bfd_session_handle_delete(bfd_node_main->bfd_session_handles[i]); + sc_metrics_handle_delete(bfd_node_main->metrics_handles[i]); + } + + FREE(bfd_node_main); + return RT_SUCCESS; +} + +/** + * @brief Create and initialize the BFD node main struct. + * + * This function allocates memory for the BFD node main struct and initializes it. + * Note: This function is not thread-safe. + * + * @param nr_graphs The number of graphs to create. + * @return Pointer to the allocated and initialized BFD node main struct, or NULL on failure. + */ +struct bfd_node_main * bfd_node_main_create(uint16_t nr_graphs) +{ + if (unlikely(nr_graphs == 0)) + { + MR_ERROR("Error creating bfd node main: invalid number of graphs."); + return NULL; + } + + struct bfd_node_main * bfd_node_main = ZMALLOC(sizeof(struct bfd_node_main)); + MR_VERIFY_MALLOC(bfd_node_main); + + bfd_node_main->nr_graphs = nr_graphs; + + bfd_node_main->bfd_session_handles = (struct bfd_session_handle **)ZMALLOC(sizeof(struct bfd_session_handle *) * + bfd_node_main->nr_graphs); + MR_VERIFY_MALLOC(bfd_node_main->bfd_session_handles); + + bfd_node_main->metrics_handles = (struct sc_metrics_handle **)ZMALLOC(sizeof(struct sc_metrics_handle *) * + bfd_node_main->nr_graphs); + MR_VERIFY_MALLOC(bfd_node_main->metrics_handles); + + for (uint16_t i = 0; i < bfd_node_main->nr_graphs; i++) + { + bfd_node_main->bfd_session_handles[i] = bfd_session_handle_create(MR_BFD_MAX_SESSION_NUM); + bfd_node_main->metrics_handles[i] = sc_metrics_handle_create(BFD_METRIC_MAX); + + if ((bfd_node_main->bfd_session_handles[i] == NULL) || (bfd_node_main->metrics_handles[i] == NULL)) + { + bfd_node_main_delete(bfd_node_main); + return NULL; + } + } + + return bfd_node_main; +} + +/** + * @brief Retrieve the global BFD node main instance. + * + * This function returns a pointer to the global BFD node main structure. + * + * @return Pointer to the global BFD node main structure. + */ +struct bfd_node_main * g_bfd_node_main_get(void) { - volatile uint64_t rx_pkts; - volatile uint64_t prev_tsc; - uint32_t ip_addr; -} __rte_cache_aligned; + return g_bfd_main; +} -/* Bfd Stat Struct */ -struct bfd_stats +/** + * @brief Set the global BFD node main instance. + * + * This function sets the global BFD node main structure. + * + * @param bfd_node_main Pointer to the BFD node main struct. + */ +int g_bfd_node_main_set(struct bfd_node_main * bfd_node_main) { - volatile uint64_t total_pkts; - volatile uint64_t pkts_per_batch; - volatile uint64_t drop_for_unexpected_pkts; - volatile uint64_t drop_for_no_free_session_id; - struct bfd_stats_for_session stats_for_session[MR_BFD_MAX_SESSION_NUM]; -} __rte_cache_aligned; - -static struct bfd_stats stats_per_graph[RTE_MAX_LCORE] = {}; - -/************************************** Bfd Node **************************************/ -/* Get Free Session Id */ -uint16_t get_free_session_id(rte_graph_t graph_id, struct rte_ipv4_hdr * ipv4_hdr) + g_bfd_main = bfd_node_main; + return RT_SUCCESS; +} + +/** + * @brief Initialize the BFD node. + * + * This function initializes the BFD node by creating the BFD node main struct. + * + * @param sc Pointer to the SC main struct. + * @return RT_SUCCESS on success, RT_ERR on failure. + */ +int bfd_init(struct sc_main * sc) { - struct bfd_stats * stats = &stats_per_graph[graph_id]; + struct bfd_node_main * bfd_node_main = bfd_node_main_create(sc->nr_io_thread); - for (int i = 0; i < MR_BFD_MAX_SESSION_NUM; i++) + if (bfd_node_main == NULL) { - if (stats->stats_for_session[i].ip_addr != 0) - continue; + return RT_ERR; + } + + unsigned int discriminator_method; + MESA_load_profile_uint_def(sc->local_cfgfile, "bfd", "disc_method", &discriminator_method, + BFD_DISCRIMINATOR_METHOD_RFC_STANDARD); + + if (discriminator_method >= BFD_DISCRIMINATOR_METHOD_MAX) + { + MR_ERROR("Error initializing bfd node: invalid discriminator method."); + bfd_node_main_delete(bfd_node_main); + return RT_ERR; + } + + bfd_node_main->discriminator_method = discriminator_method; + g_bfd_node_main_set(bfd_node_main); + + MR_INFO("Bfd init success"); + if (discriminator_method == 0) + MR_INFO("Bfd discriminator method: RFC Standard"); + else + MR_INFO("Bfd discriminator method: Flipping"); + + return RT_SUCCESS; +} + +/** + * @brief Deinitialize the BFD node. + * + * This function deinitializes the BFD node by deleting the BFD node main struct. + * + * @param sc Pointer to the SC main struct. + */ + +void bfd_deinit(struct sc_main * sc) +{ + bfd_node_main_delete(g_bfd_main); + g_bfd_node_main_set(NULL); +} + +/************************************************* Bfd session handle *************************************************/ +/* BFD session structure */ +struct bfd_session +{ + volatile uint64_t prev_tsc; // Timestamp of the previous BFD packet + uint32_t ipv4_address; // IPv4 address of the remote peer + uint32_t my_discriminator; // Discriminator assigned to this session + uint64_t reserved[6]; // Reserved for future use or alignment +} __rte_cache_aligned; // Ensure the structure is cache-aligned + +/* BFD session handle structure */ +struct bfd_session_handle +{ + struct bfd_session * bfd_sessions; // Pointer to an array of BFD sessions + uint16_t max_sessions; // Maximum number of sessions supported + uint16_t current_sessions; // Number of active sessions currently +}; + +/** + * @brief Create and initialize a BFD session handle. + * + * This function allocates memory for the BFD session handle and initializes it. + * Note: This function is not thread-safe. + * + * @param max_sessions The maximum number of sessions that can be stored. + * @return Pointer to the allocated and initialized session handle, or NULL on failure. + */ +struct bfd_session_handle * bfd_session_handle_create(uint16_t max_sessions) +{ + if (unlikely(max_sessions == 0)) + { + MR_ERROR("Error creating bfd session handle: invalid number of sessions."); + return NULL; + } + + struct bfd_session_handle * bfd_session_handle = (struct bfd_session_handle *)ZMALLOC( + sizeof(struct bfd_session_handle)); + MR_VERIFY_MALLOC(bfd_session_handle); + + bfd_session_handle->bfd_sessions = (struct bfd_session *)ZMALLOC(sizeof(struct bfd_session) * max_sessions); + MR_VERIFY_MALLOC(bfd_session_handle->bfd_sessions); + + bfd_session_handle->max_sessions = max_sessions; + bfd_session_handle->current_sessions = 0; + + return bfd_session_handle; +} + +/** + * @brief Delete a BFD session handle. + * + * This function frees the memory allocated for the BFD session handle. + * Note: This function is not thread-safe. + * + * @param bfd_session_handle Pointer to the BFD session handle to delete. + */ +int bfd_session_handle_delete(struct bfd_session_handle * bfd_session_handle) +{ + if (bfd_session_handle == NULL) + return RT_ERR; + + FREE(bfd_session_handle->bfd_sessions); + bfd_session_handle->bfd_sessions = NULL; + + FREE(bfd_session_handle); + return RT_SUCCESS; +} + +/** + * @brief Get the BFD session handle. + * + * This function returns the BFD session handle for the specified graph ID. + * + * @param bfd_node_main Pointer to the BFD node main struct. + * @param graph_id The graph ID. + * @return Pointer to the BFD session handle. + */ +struct bfd_session_handle * bfd_session_handle_get(struct bfd_node_main * bfd_node_main, uint16_t graph_id) +{ + assert(bfd_node_main != NULL); + assert(graph_id < bfd_node_main->nr_graphs); + + return bfd_node_main->bfd_session_handles[graph_id]; +} + +/** + * @brief Check if a BFD session handle is empty. + * + * This function checks if a BFD session handle is empty. + * + * @param bfd_session_handle Pointer to the BFD session handle. + * @return 1 if the session handle is empty, 0 otherwise. + */ +int bfd_session_handle_is_empty(struct bfd_session_handle * bfd_session_handle) +{ + assert(bfd_session_handle != NULL); + + return bfd_session_handle->current_sessions == 0; +} + +/** + * @brief Check if a BFD session handle is full. + * + * This function checks if a BFD session handle is full. + * + * @param bfd_session_handle Pointer to the BFD session handle. + * @return 1 if the session handle is full, 0 otherwise. + */ +static inline int __bfd_session_handle_is_full(struct bfd_session_handle * bfd_session_handle) +{ + assert(bfd_session_handle != NULL); + + return bfd_session_handle->current_sessions == bfd_session_handle->max_sessions; +} - stats->stats_for_session[i].ip_addr = ipv4_hdr->src_addr; - return i; +/** + * @brief Check if a BFD session handle is full. + * + * This function checks if a BFD session handle is full. + * + * @param bfd_session_handle Pointer to the BFD session handle. + * @return 1 if the session handle is full, 0 otherwise. + */ +int bfd_session_handle_is_full(struct bfd_session_handle * bfd_session_handle) +{ + return __bfd_session_handle_is_full(bfd_session_handle); +} + +/** + * @brief Lookup for a BFD session by IPv4 address. + * + * This function lookups for a BFD session with the specified IPv4 address. + * Note: This function is not thread-safe. + * + * @param bfd_session_handle Pointer to the BFD session handle. + * @param ipv4_address The IPv4 address to lookup for. + * @return Pointer to the BFD session with the specified IPv4 address, or NULL if not found. + */ +struct bfd_session * bfd_session_lookup(struct bfd_session_handle * bfd_session_handle, uint32_t ipv4_address) +{ + assert(bfd_session_handle != NULL); + + struct bfd_session * bfd_session = NULL; + for (uint16_t i = 0; i < bfd_session_handle->max_sessions; i++) + { + bfd_session = &bfd_session_handle->bfd_sessions[i]; + if (bfd_session->ipv4_address == ipv4_address) + { + return &bfd_session_handle->bfd_sessions[i]; + } } - return UINT16_MAX; + + return NULL; } -/* Get Bfd Session Id */ -uint16_t get_bfd_session_id(rte_graph_t graph_id, struct rte_ipv4_hdr * ipv4_hdr) +/** + * @brief Add a BFD session to the session handle. + * + * This function adds a BFD session to the session handle. + * Note: This function is not thread-safe. + * + * @param bfd_session_handle Pointer to the BFD session handle. + * @param ipv4_address The IPv4 address of the session to add. + * @return Pointer to the added BFD session, or NULL if no free session ID. + */ +struct bfd_session * bfd_session_add(struct bfd_session_handle * bfd_session_handle, uint32_t ipv4_address) { - struct bfd_stats * stats = &stats_per_graph[graph_id]; + assert(bfd_session_handle != NULL); - for (int i = 0; i < MR_BFD_MAX_SESSION_NUM; i++) + if (unlikely(__bfd_session_handle_is_full(bfd_session_handle))) { - if (stats->stats_for_session[i].ip_addr == ipv4_hdr->src_addr) - return i; + MR_ERROR("Error adding bfd session: no free session id."); + return NULL; } - return get_free_session_id(graph_id, ipv4_hdr); + struct bfd_session * bfd_session = NULL; + for (uint16_t i = 0; i < bfd_session_handle->max_sessions; i++) + { + bfd_session = &bfd_session_handle->bfd_sessions[i]; + if (bfd_session->ipv4_address == 0) + { + bfd_session->ipv4_address = ipv4_address; + bfd_session->prev_tsc = 0; + bfd_session->my_discriminator = htonl(MR_BFD_START_DISCRIMINATOR + i); + + bfd_session_handle->current_sessions++; + + return bfd_session; + } + } + + return NULL; +} + +/** + * @brief Update a BFD session. + * + * This function updates a BFD session. + * Note: This function is not thread-safe. + * + * @param bfd_session Pointer to the BFD session to update. + */ +static inline int __bfd_session_update(struct bfd_session * bfd_session) +{ + assert(bfd_session != NULL); + + bfd_session->prev_tsc = rte_rdtsc(); + return RT_SUCCESS; } -#define BFD_USE_YOUR_DISCRIMINATOR_AS_MY_DISCRIMINATOR 0 +/** + * @brief Update a BFD session. + * + * This function updates a BFD session. + * Note: This function is not thread-safe. + * + * @param bfd_session Pointer to the BFD session to update. + */ +int bfd_session_update(struct bfd_session * bfd_session) +{ + return __bfd_session_update(bfd_session); +} -/* Reply Bfd Request */ -uint8_t bfd_reply(struct rte_ether_hdr * ether_hdr, struct rte_ipv4_hdr * ipv4_hdr, struct rte_udp_hdr * udp_hdr, - uint16_t bfd_session_id) +/** + * @brief Delete a BFD session from the session handle. + * + * This function deletes a BFD session from the session handle. + * Note: This function is not thread-safe. + * + * @param bfd_session_handle Pointer to the BFD session handle. + * @param ipv4_address The IPv4 address of the session to delete. + */ +int bfd_session_delete(struct bfd_session_handle * bfd_session_handle, uint32_t ipv4_address) +{ + assert(bfd_session_handle != NULL); + + if (unlikely(bfd_session_handle_is_empty(bfd_session_handle))) + { + MR_ERROR("Error deleting bfd session: session handle is empty."); + return RT_ERR; + } + + struct bfd_session * bfd_session = NULL; + for (uint16_t i = 0; i < bfd_session_handle->max_sessions; i++) + { + bfd_session = &bfd_session_handle->bfd_sessions[i]; + if (bfd_session->ipv4_address == ipv4_address) + { + memset(bfd_session, 0, sizeof(struct bfd_session)); + bfd_session_handle->current_sessions--; + return RT_SUCCESS; + } + } + + return RT_ERR; +} + +/************************************************ Auxiliary functions *************************************************/ +/** + * @brief Prepare a BFD packet reply by swapping headers and updating checksums. + * + * This function processes an incoming BFD packet and prepares a reply by performing + * the following steps: + * - Swap the source and destination Ethernet addresses in the Ethernet header. + * - Swap the source and destination IP addresses in the IPv4 header, and reset the TTL. + * - Recalculate the IPv4 header checksum. + * - Swap the discriminators in the BFD header based on the configuration flag + * `BFD_USE_YOUR_DISCRIMINATOR_AS_MY_DISCRIMINATOR`. + * - Reset the UDP checksum. + * - Update the BFD state based on the current state and transition it appropriately. + * + * @param ether_hdr Pointer to the Ethernet header. + * @param ipv4_hdr Pointer to the IPv4 header. + * @param udp_hdr Pointer to the UDP header. + * @param bfd_hdr Pointer to the BFD header. + * @param bfd_session Pointer to the BFD session structure. + * + * @return The previous BFD state. + */ +uint8_t bfd_pkt_reply(struct rte_ether_hdr * ether_hdr, struct rte_ipv4_hdr * ipv4_hdr, struct rte_udp_hdr * udp_hdr, + struct bfd_hdr * bfd_hdr, struct bfd_session * bfd_session) { /* Swap ether_hdr */ struct rte_ether_addr swap_addr; @@ -123,24 +512,24 @@ uint8_t bfd_reply(struct rte_ether_hdr * ether_hdr, struct rte_ipv4_hdr * ipv4_h ipv4_hdr->hdr_checksum = rte_ipv4_cksum(ipv4_hdr); /* Swap discriminator */ - struct bfd_header_t * bfd_hdr = (struct bfd_header_t *)(udp_hdr + 1); - uint8_t swap_discriminator[4]; - -#if BFD_USE_YOUR_DISCRIMINATOR_AS_MY_DISCRIMINATOR - memcpy(swap_discriminator, bfd_hdr->your_discriminator, sizeof(swap_discriminator)); - memcpy(bfd_hdr->your_discriminator, bfd_hdr->my_discriminator, sizeof(swap_discriminator)); - memcpy(bfd_hdr->my_discriminator, swap_discriminator, sizeof(swap_discriminator)); -#else - uint32_t discriminator = htonl(MR_BFD_START_DISCRIMINATOR + bfd_session_id); - memcpy(swap_discriminator, bfd_hdr->my_discriminator, sizeof(swap_discriminator)); - memcpy(bfd_hdr->my_discriminator, &discriminator, sizeof(swap_discriminator)); - memcpy(bfd_hdr->your_discriminator, swap_discriminator, sizeof(swap_discriminator)); -#endif + + if (g_bfd_main->discriminator_method == BFD_DISCRIMINATOR_METHOD_RFC_STANDARD) + { + rte_memcpy(bfd_hdr->your_discriminator, bfd_hdr->my_discriminator, sizeof(bfd_hdr->my_discriminator)); + rte_memcpy(bfd_hdr->my_discriminator, &bfd_session->my_discriminator, sizeof(bfd_hdr->my_discriminator)); + } + else + { + uint8_t swap_discriminator[4]; + rte_memcpy(swap_discriminator, bfd_hdr->your_discriminator, sizeof(swap_discriminator)); + rte_memcpy(bfd_hdr->your_discriminator, bfd_hdr->my_discriminator, sizeof(swap_discriminator)); + rte_memcpy(bfd_hdr->my_discriminator, swap_discriminator, sizeof(swap_discriminator)); + } /* Set udp check sum */ udp_hdr->dgram_cksum = 0; - /* Set Bfd State */ + /* Set Bfd state */ uint8_t bfd_state = bfd_hdr->state; if (bfd_state == BFD_STATE_DOWN) { @@ -156,15 +545,8 @@ uint8_t bfd_reply(struct rte_ether_hdr * ether_hdr, struct rte_ipv4_hdr * ipv4_h return bfd_state; } -/* Bfd Node Init Function */ -static int bfd_node_init(const struct rte_graph * graph, struct rte_node * node) -{ - return 0; -} - -static __rte_always_inline void gen_store_trace_info(struct rte_node * node, struct rte_mbuf * mbuf, - uint16_t next_node_index, int expect_result, - uint16_t bfd_session_id, uint8_t bfd_state) +void gen_store_trace_info(struct rte_node * node, struct rte_mbuf * mbuf, uint16_t next_node_index, int expect_result, + uint8_t bfd_state, struct bfd_session * bfd_session) { /* Populate the next node infomation */ char str_record[MR_STRING_MAX]; @@ -177,7 +559,7 @@ static __rte_always_inline void gen_store_trace_info(struct rte_node * node, str { len += snprintf(str_record + len, sizeof(str_record) - len, ", rsn:pkt noncompliant with bfd"); } - else if (bfd_session_id == UINT16_MAX) + else if (bfd_session == NULL) { len += snprintf(str_record + len, sizeof(str_record) - len, ", rsn:no free sessionid"); } @@ -192,132 +574,22 @@ static __rte_always_inline void gen_store_trace_info(struct rte_node * node, str dp_trace_record_emit_str(sc_main_get()->trace, mbuf, rte_lcore_id(), &meta, str_record); } -/* Bfd Node Process Function */ -static __rte_always_inline uint16_t bfd_node_process(struct rte_graph * graph, struct rte_node * node, void ** objs, - uint16_t cnt) -{ - uint16_t last_spec = 0, next_node_index = 0; - uint16_t n_left_from = cnt; - struct rte_mbuf * mbuf; - struct rte_mbuf ** pkts = (struct rte_mbuf **)objs; - void ** batch_pkts = objs; - uint16_t batch_next_node_index = BFD_NEXT_ETH_EGRESS; - uint64_t drop_for_unexpected_pkts = 0; - uint64_t drop_for_no_free_session_id = 0; - - /* Single Packet Processing */ - while (n_left_from > 0) - { - mbuf = pkts[0]; - pkts += 1; - n_left_from -= 1; - - /* Get Private Data */ - uint8_t bfd_state = BFD_STATE_ADMINDOWN; - uint16_t bfd_session_id = UINT16_MAX; - int expect_result = RT_ERR; - - struct mrb_metadata * mrb_meta = mrbuf_cz_data(mbuf, MR_NODE_CTRLZONE_ID); - struct pkt_parser_result * pkt_parser_result = &mrb_meta->pkt_parser_result; - - static const uint16_t expect_layers[] = { - LAYER_TYPE_ID_ETHER, - LAYER_TYPE_ID_IPV4, - LAYER_TYPE_ID_UDP, - }; - - expect_result = complex_layer_type_expect(pkt_parser_result, expect_layers, RTE_DIM(expect_layers)); - if (unlikely(expect_result < 0)) - { - drop_for_unexpected_pkts++; - next_node_index = BFD_NEXT_PKT_DROP; - goto node_enqueue; - } - - struct rte_ether_hdr * ether_hdr = rte_pktmbuf_mtod(mbuf, struct rte_ether_hdr *); - struct rte_ipv4_hdr * ipv4_hdr = - complex_layer_jump_to_outermost(pkt_parser_result, rte_pktmbuf_mtod(mbuf, void *), LAYER_TYPE_ID_IPV4); - struct rte_udp_hdr * udp_hdr = - complex_layer_jump_to_outermost(pkt_parser_result, rte_pktmbuf_mtod(mbuf, void *), LAYER_TYPE_ID_UDP); - - assert(ipv4_hdr != NULL); - assert(udp_hdr != NULL); - - /* Get Session Id */ - bfd_session_id = get_bfd_session_id(graph->id, ipv4_hdr); - if (unlikely(bfd_session_id == UINT16_MAX)) - { - drop_for_no_free_session_id++; - next_node_index = BFD_NEXT_PKT_DROP; - goto node_enqueue; - } - - /* Reply Bfd Request */ - bfd_state = bfd_reply(ether_hdr, ipv4_hdr, udp_hdr, bfd_session_id); - stats_per_graph[graph->id].stats_for_session[bfd_session_id].rx_pkts++; - - /* Update prev_tsc */ - stats_per_graph[graph->id].stats_for_session[bfd_session_id].prev_tsc = rte_rdtsc(); - - /* From Ingress Port Id Get Next Node Index (TX) */ - mrb_meta->port_egress = mrb_meta->port_ingress; - next_node_index = BFD_NEXT_ETH_EGRESS; - - node_enqueue: - /* Check if tracing is enabled for the current Mbuf */ - if (unlikely(dp_trace_record_can_emit(mbuf, DP_TRACE_MEASUREMENT_TYPE_TRACE))) - { - gen_store_trace_info(node, mbuf, next_node_index, expect_result, bfd_session_id, bfd_state); - } - - /* Judge The Next Index Whether To Change */ - if (unlikely(batch_next_node_index != next_node_index)) - { - /* If The Next Index Has Been Changed,Enqueue Last Pkts */ - rte_node_enqueue(graph, node, batch_next_node_index, batch_pkts, last_spec); - batch_pkts += last_spec; - last_spec = 1; - batch_next_node_index = next_node_index; - } - else - { - /* If The Next Index Not Change, Update The Lasts */ - last_spec++; - } - } - - /* Update graph stats */ - struct bfd_stats * graph_stats = &stats_per_graph[graph->id]; - graph_stats->total_pkts += cnt; - graph_stats->pkts_per_batch = cnt; - graph_stats->drop_for_unexpected_pkts += drop_for_unexpected_pkts; - graph_stats->drop_for_no_free_session_id += drop_for_no_free_session_id; - - /* Process The Remaining Packets */ - if (likely(last_spec > 0)) - rte_node_enqueue(graph, node, batch_next_node_index, batch_pkts, last_spec); - return cnt; -} - -/* Bfd Node Base */ -static struct rte_node_register bfd_node_base = { - .process = bfd_node_process, - .name = "bfd", - .init = bfd_node_init, - .nb_edges = BFD_NEXT_MAX, - .next_nodes = - { - [BFD_NEXT_PKT_DROP] = "pkt_drop_trap", - [BFD_NEXT_ETH_EGRESS] = "eth_egress", - }, -}; -RTE_NODE_REGISTER(bfd_node_base); - -/************************************** Bfd Statistics **************************************/ +/** + * @brief Create a JSON object from an array of 64-bit unsigned integers. + * + * This function creates a JSON object from an array of 64-bit unsigned integers. + * + * @param array The array of 64-bit unsigned integers. + * @param nr_elements The number of elements in the array. + * @return Pointer to the created JSON object. + */ cJSON * bfd_node_monit_loop(struct sc_main * sc) { cJSON * json_root = cJSON_CreateObject(); unsigned int nr_graphs = sc->nr_io_thread; + struct bfd_node_main * bfd_node_main = g_bfd_node_main_get(); + + assert(nr_graphs == bfd_node_main->nr_graphs); uint64_t total_pkts[nr_graphs]; uint64_t pkts_per_batch[nr_graphs]; @@ -326,8 +598,10 @@ cJSON * bfd_node_monit_loop(struct sc_main * sc) for (uint32_t graph_id = 0; graph_id < nr_graphs; graph_id++) { - struct bfd_stats * stats = &stats_per_graph[graph_id]; - if (stats->total_pkts == 0) + uint64_t values[BFD_METRIC_MAX]; + sc_metrics_values_get(bfd_node_main->metrics_handles[graph_id], values, BFD_METRIC_MAX); + + if (values[BFD_METRIC_TOT_PKTS] == 0) { total_pkts[graph_id] = 0; pkts_per_batch[graph_id] = 0; @@ -336,10 +610,10 @@ cJSON * bfd_node_monit_loop(struct sc_main * sc) continue; } - total_pkts[graph_id] = stats->total_pkts; - pkts_per_batch[graph_id] = stats->pkts_per_batch; - unexpected_pkts[graph_id] = stats->drop_for_unexpected_pkts; - no_free_session_id[graph_id] = stats->drop_for_no_free_session_id; + total_pkts[graph_id] = values[BFD_METRIC_TOT_PKTS]; + pkts_per_batch[graph_id] = values[BFD_METRIC_PKTS_BATCH]; + unexpected_pkts[graph_id] = values[BFD_METRIC_DROP_UNEXP_PKTS]; + no_free_session_id[graph_id] = values[BFD_METRIC_DROP_NO_FREE_SESS_PKTS]; } cJSON * json_total_pkts = create_uint64_array(total_pkts, nr_graphs); @@ -355,41 +629,42 @@ cJSON * bfd_node_monit_loop(struct sc_main * sc) cJSON_AddItemToObject(json_root, "bfd, drop_for_no_free_session_id", json_no_free_session_id); uint32_t session_num = 0; - struct bfd_stats_for_session sessions[MR_BFD_MAX_SESSION_NUM] = {}; + struct bfd_session sessions[MR_BFD_MAX_SESSION_NUM] = {}; const uint64_t drain_tsc = (rte_get_tsc_hz() + US_PER_S - 1) / US_PER_S * 500000; for (uint32_t graph_id = 0; graph_id < nr_graphs; ++graph_id) { - struct bfd_stats * stats = &stats_per_graph[graph_id]; - if (stats->total_pkts == 0) + struct bfd_session_handle * bfd_session_handle = bfd_node_main->bfd_session_handles[graph_id]; + if (bfd_session_handle->current_sessions == 0) continue; - for (int i = 0; i < MR_BFD_MAX_SESSION_NUM; ++i) + for (int i = 0; i < bfd_session_handle->max_sessions; ++i) { - struct bfd_stats_for_session * current_session = &stats->stats_for_session[i]; - if (current_session->ip_addr == 0) + struct bfd_session * session = &bfd_session_handle->bfd_sessions[i]; + if (session->ipv4_address == 0) continue; bool should_add = true; for (int j = 0; j < session_num; ++j) { - if (sessions[j].ip_addr != current_session->ip_addr) + if (sessions[j].ipv4_address != session->ipv4_address) continue; should_add = false; - if (current_session->prev_tsc > sessions[j].prev_tsc) + + /* Update the session tsc */ + if (session->prev_tsc > sessions[j].prev_tsc) { - sessions[j].rx_pkts += current_session->rx_pkts; - sessions[j].prev_tsc = current_session->prev_tsc; + sessions[j].prev_tsc = session->prev_tsc; } + break; } - if (should_add && session_num < MR_BFD_MAX_SESSION_NUM) + if (should_add && session_num < bfd_session_handle->max_sessions) { - sessions[session_num].rx_pkts = current_session->rx_pkts; - sessions[session_num].ip_addr = current_session->ip_addr; - sessions[session_num].prev_tsc = current_session->prev_tsc; + sessions[session_num].ipv4_address = session->ipv4_address; + sessions[session_num].prev_tsc = session->prev_tsc; ++session_num; } } @@ -398,11 +673,10 @@ cJSON * bfd_node_monit_loop(struct sc_main * sc) for (int i = 0; i < session_num; ++i) { struct in_addr ip_addr; - ip_addr.s_addr = sessions[i].ip_addr; + ip_addr.s_addr = sessions[i].ipv4_address; cJSON * session_obj = cJSON_CreateObject(); cJSON_AddStringToObject(session_obj, "ip_addr", inet_ntoa(ip_addr)); - cJSON_AddNumberToObject(session_obj, "rx_pkts", sessions[i].rx_pkts); uint64_t prev_tsc = sessions[i].prev_tsc; uint64_t cur_tsc = rte_rdtsc(); @@ -420,3 +694,135 @@ cJSON * bfd_node_monit_loop(struct sc_main * sc) cJSON_AddNumberToObject(json_root, "total_session_num", session_num); return json_root; } + +/************************************************** BFD node process **************************************************/ +/** + * @brief Process the BFD node. + * + * This function processes the BFD node by handling incoming BFD packets and replying to them. + * + * @param graph Pointer to the graph. + * @param node Pointer to the node. + * @param objs Array of pointers to objects. + * @param cnt The number of objects in the array. + * @return The number of objects processed. + */ +static uint16_t bfd_node_process(struct rte_graph * graph, struct rte_node * node, void ** objs, uint16_t cnt) +{ + uint16_t last_spec = 0; + uint16_t n_left_from = cnt; + uint16_t batch_next_node_index = BFD_NEXT_ETH_EGRESS; + struct rte_mbuf ** pkts = (struct rte_mbuf **)objs; + void ** batch_pkts = objs; + + uint64_t inc_values[BFD_METRIC_MAX] = {0}; + + struct bfd_session_handle * bfd_session_handle = g_bfd_main->bfd_session_handles[graph->id]; + + /* Single packet processing */ + while (n_left_from > 0) + { + struct rte_mbuf * mbuf = pkts[0]; + pkts += 1; + n_left_from -= 1; + + struct mrb_metadata * mrb_meta = mrbuf_cz_data(mbuf, MR_NODE_CTRLZONE_ID); + struct pkt_parser_result * pkt_parser_result = &mrb_meta->pkt_parser_result; + + static const uint16_t expect_layers[] = { + LAYER_TYPE_ID_ETHER, + LAYER_TYPE_ID_IPV4, + LAYER_TYPE_ID_UDP, + }; + + uint16_t next_node_index = BFD_NEXT_ETH_EGRESS; + int expect_result = complex_layer_type_expect(pkt_parser_result, expect_layers, RTE_DIM(expect_layers)); + if (unlikely(expect_result < 0)) + { + inc_values[BFD_METRIC_DROP_UNEXP_PKTS]++; + next_node_index = BFD_NEXT_PKT_DROP; + goto node_enqueue; + } + + struct rte_ether_hdr * ether_hdr = rte_pktmbuf_mtod(mbuf, struct rte_ether_hdr *); + struct rte_ipv4_hdr * ipv4_hdr = complex_layer_jump_to_outermost( + pkt_parser_result, rte_pktmbuf_mtod(mbuf, void *), LAYER_TYPE_ID_IPV4); + struct rte_udp_hdr * udp_hdr = complex_layer_jump_to_outermost( + pkt_parser_result, rte_pktmbuf_mtod(mbuf, void *), LAYER_TYPE_ID_UDP); + struct bfd_hdr * bfd_hdr = (struct bfd_hdr *)(udp_hdr + 1); + + assert(ipv4_hdr != NULL); + assert(udp_hdr != NULL); + assert(bfd_hdr != NULL); + + /* Bfd session search */ + struct bfd_session * bfd_session = bfd_session_lookup(bfd_session_handle, ipv4_hdr->src_addr); + + if (unlikely(bfd_session == NULL)) + { + bfd_session = bfd_session_add(bfd_session_handle, ipv4_hdr->src_addr); + if (unlikely(bfd_session == NULL)) + { + inc_values[BFD_METRIC_DROP_NO_FREE_SESS_PKTS]++; + next_node_index = BFD_NEXT_PKT_DROP; + goto node_enqueue; + } + } + + /* Reply bfd request */ + uint8_t bfd_state = bfd_pkt_reply(ether_hdr, ipv4_hdr, udp_hdr, bfd_hdr, bfd_session); + + /* Update bfd session prev_tsc */ + __bfd_session_update(bfd_session); + + /* From ingress port id get next node index (TX) */ + mrb_meta->port_egress = mrb_meta->port_ingress; + next_node_index = BFD_NEXT_ETH_EGRESS; + + node_enqueue: + /* Check if tracing is enabled for the current mbuf */ + if (unlikely(dp_trace_record_can_emit(mbuf, DP_TRACE_MEASUREMENT_TYPE_TRACE))) + { + gen_store_trace_info(node, mbuf, next_node_index, expect_result, bfd_state, bfd_session); + } + + /* Judge the next index whether to change */ + if (unlikely(batch_next_node_index != next_node_index)) + { + /* If the next index has been changed, enqueue last pkts */ + rte_node_enqueue(graph, node, batch_next_node_index, batch_pkts, last_spec); + batch_pkts += last_spec; + last_spec = 1; + batch_next_node_index = next_node_index; + } + else + { + /* If the next index not change, update the lasts */ + last_spec++; + } + } + + /* Update metrics */ + inc_values[BFD_METRIC_TOT_PKTS] += cnt; + sc_metrics_accumulate(g_bfd_main->metrics_handles[graph->id], inc_values, RTE_DIM(inc_values)); + sc_metrics_value_set(g_bfd_main->metrics_handles[graph->id], cnt, BFD_METRIC_PKTS_BATCH); + + /* Process the remaining packets */ + if (likely(last_spec > 0)) + rte_node_enqueue(graph, node, batch_next_node_index, batch_pkts, last_spec); + return cnt; +} + +/* Bfd node base */ +static struct rte_node_register bfd_node_base = { + .process = bfd_node_process, + .name = "bfd", + .init = NULL, + .nb_edges = BFD_NEXT_MAX, + .next_nodes = + { + [BFD_NEXT_PKT_DROP] = "pkt_drop_trap", + [BFD_NEXT_ETH_EGRESS] = "eth_egress", + }, +}; +RTE_NODE_REGISTER(bfd_node_base); diff --git a/service/src/sc_metrics.c b/service/src/sc_metrics.c new file mode 100644 index 0000000..72dc9ea --- /dev/null +++ b/service/src/sc_metrics.c @@ -0,0 +1,221 @@ +#include <rte_memcpy.h> + +#include <sc_metrics.h> + +/** + * @brief Round up a value to the nearest multiple of 8. + * Note: This function is not thread-safe. + * + * @param value The value to round up. + * @return The rounded up value. + */ +uint16_t round_up_to_multiple_of_8(uint16_t value) +{ + return (value + 7) & ~7; +} + +/** + * @brief Create and initialize a new SC metrics handle. + * + * This function allocates and initializes a new SC metrics handle with the + * specified capacity. It ensures that the capacity is rounded up to the + * nearest multiple of 8. The handle includes memory for storing metrics + * values based on the rounded capacity. + * + * @param capacity The number of metrics values that the handle should be able + * to store. This value is rounded up to the nearest multiple + * of 8. + * + * @return Pointer to the newly created SC metrics handle, or NULL if memory + * allocation fails or if an invalid capacity is provided (0). + */ +struct sc_metrics_handle * sc_metrics_handle_create(uint16_t capacity) +{ + if (capacity == 0) + { + MR_ERROR("Error allocating stats handle: invalid input parameters."); + return NULL; + } + else if (capacity > SC_METRICS_MAX_CAPACITY) + { + MR_ERROR("Error allocating stats handle: capacity exceeds maximum limit."); + return NULL; + } + + struct sc_metrics_handle * metrics_handle = (struct sc_metrics_handle *)ZMALLOC(sizeof(struct sc_metrics_handle)); + MR_VERIFY_MALLOC(metrics_handle); + + metrics_handle->capacity = round_up_to_multiple_of_8(capacity); + + metrics_handle->values = (volatile uint64_t *)ZMALLOC(sizeof(uint64_t) * metrics_handle->capacity); + MR_VERIFY_MALLOC(metrics_handle->values); + + return metrics_handle; +} + +/** + * @brief Delete and free an SC metrics handle. + * + * This function deallocates the memory associated with an SC metrics handle, + * including its values array. After freeing the memory, it sets the pointers + * to NULL to avoid dangling pointers. + * + * @param metrics_handle Pointer to the SC metrics handle to be deleted. + * If this pointer is NULL, no action is taken. + * + * @return Return value indicating success or failure: + * - RT_SUCCESS (0) if the handle is successfully deleted. + * - RT_ERR (-1) if the handle is NULL and cannot be deleted. + */ +int sc_metrics_handle_delete(struct sc_metrics_handle * metrics_handle) +{ + if (metrics_handle == NULL) + { + MR_ERROR("Error deleting stats handle: invalid input parameters."); + return RT_ERR; + } + + FREE((void *)metrics_handle->values); + metrics_handle->values = NULL; + + FREE((void *)metrics_handle); + + return RT_SUCCESS; +} + +/** + * @brief Set the values for the SC metrics handle. + * + * This function copies an array of metric values into the SC metrics handle. + * It ensures that the provided capacity does not exceed the handle's allocated + * capacity. The function uses `rte_memcpy` to perform the copy operation. + * + * @param metrics_handle Pointer to the SC metrics handle where the values will + * be set. This handle must be valid and initialized. + * @param stats Array of metric values to be copied into the handle. + * @param capacity Number of metric values to copy from the `stats` array. + * This should not exceed the handle's allocated capacity. + * + * @return Return value indicating success: + * - RT_SUCCESS (0) if the values are successfully copied. + */ +int sc_metrics_values_set(struct sc_metrics_handle * metrics_handle, uint64_t values[], uint16_t capacity) +{ + assert(metrics_handle != NULL); + assert(metrics_handle->values != NULL); + assert(capacity <= metrics_handle->capacity); + + rte_memcpy((void *)metrics_handle->values, values, sizeof(uint64_t) * capacity); + return RT_SUCCESS; +} + +/** + * @brief Get the values from the SC metrics handle. + * + * This function copies the metric values from the SC metrics handle into the + * provided array. It ensures that the provided capacity does not exceed the + * handle's allocated capacity. The function uses `rte_memcpy` to perform the + * copy operation. + * + * @param metrics_handle Pointer to the SC metrics handle from which the values + * will be retrieved. This handle must be valid and initialized. + * @param values Array where the metric values will be copied. + * @param capacity Number of metric values to copy into the `values` array. + * This should not exceed the handle's allocated capacity. + * + * @return Return value indicating success: + * - RT_SUCCESS (0) if the values are successfully copied. + */ +int sc_metrics_values_get(struct sc_metrics_handle * metrics_handle, uint64_t values[], uint16_t capacity) +{ + assert(metrics_handle != NULL); + assert(metrics_handle->values != NULL); + assert(capacity <= metrics_handle->capacity); + + rte_memcpy(values, (void *)metrics_handle->values, sizeof(uint64_t) * capacity); + return RT_SUCCESS; +} + +/** + * @brief Get a single value from the SC metrics handle. + * + * This function retrieves a single metric value from the SC metrics handle at + * the specified key. The function ensures that the key is within the handle's + * allocated capacity. The value is directly read from the handle's values array + * at the specified key. + * + * @param metrics_handle Pointer to the SC metrics handle from which the value + * will be retrieved. This handle must be valid and initialized. + * @param value Pointer to the variable where the metric value will be stored. + * @param key The key from which to retrieve the metric value. This key must be + * within the handle's allocated capacity. + * + * @return Return value indicating success: + * - RT_SUCCESS (0) if the value is successfully retrieved. + */ +int sc_metrics_value_get(struct sc_metrics_handle * metrics_handle, uint64_t * value, uint16_t key) +{ + assert(metrics_handle != NULL); + assert(metrics_handle->values != NULL); + assert(key < metrics_handle->capacity); + + *value = metrics_handle->values[key]; + return RT_SUCCESS; +} + +/** + * @brief Reset all values in the SC metrics handle to zero. + * + * This function sets all values in the SC metrics handle to zero. It uses + * `memset` to overwrite the entire allocated space for metric values with zeros. + * This operation effectively resets the metrics handle to its initial state. + * + * @param metrics_handle Pointer to the SC metrics handle to be reset. + * This handle must be valid and initialized. + * + * @return Return value indicating success: + * - RT_SUCCESS (0) if the values are successfully reset. + */ +int sc_metrics_values_reset(struct sc_metrics_handle * metrics_handle) +{ + assert(metrics_handle != NULL); + assert(metrics_handle->values != NULL); + + memset((void *)metrics_handle->values, 0, sizeof(uint64_t) * metrics_handle->capacity); + return RT_SUCCESS; +} + +/** + * @brief Accumulate values into the SC metrics handle. + * + * This function adds values from the `inc_values` array to the existing values + * in the SC metrics handle. The accumulation is performed element-wise for + * the specified `capacity`. The function ensures that the provided capacity + * does not exceed the handle's allocated capacity. + * + * @param metrics_handle Pointer to the SC metrics handle where the values + * will be accumulated. This handle must be valid and + * initialized. + * @param inc_values Array of values to be added to the current values in + * the handle. + * @param capacity Number of values to accumulate. This should not exceed + * the handle's allocated capacity. + * + * @return Return value indicating success: + * - RT_SUCCESS (0) if the values are successfully accumulated. + */ +int sc_metrics_accumulate(struct sc_metrics_handle * metrics_handle, uint64_t inc_values[], uint16_t capacity) +{ + assert(metrics_handle != NULL); + assert(metrics_handle->values != NULL); + assert(capacity <= metrics_handle->capacity); + + volatile uint64_t * values = metrics_handle->values; + + for (uint16_t i = 0; i < capacity; i++) + { + values[i] += inc_values[i]; + } + + return RT_SUCCESS; +} diff --git a/service/test/test_common.c b/service/test/test_common.c new file mode 100644 index 0000000..4d9f20d --- /dev/null +++ b/service/test/test_common.c @@ -0,0 +1,167 @@ +#include <rte_ether.h> +#include <rte_ip.h> +#include <rte_mbuf.h> +#include <rte_udp.h> + +#include <sc_common.h> + +/** + * @brief Construct an Ethernet header + * + * @param eth_hdr Pointer to the Ethernet header + * @param src_mac Source MAC address + * @param dst_mac Destination MAC address + * @param eth_type Ethernet type + */ +void eth_header_construct(struct rte_ether_hdr * eth_hdr, struct rte_ether_addr * src_mac, + struct rte_ether_addr * dst_mac, uint16_t eth_type) +{ + const struct rte_ether_addr default_src_mac = {.addr_bytes = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}}; + const struct rte_ether_addr default_dst_mac = {.addr_bytes = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66}}; + + if (src_mac == NULL) + { + rte_memcpy(ð_hdr->src_addr, &default_src_mac, sizeof(struct rte_ether_addr)); + } + else + { + rte_memcpy(ð_hdr->src_addr, src_mac, sizeof(struct rte_ether_addr)); + } + + if (dst_mac == NULL) + { + rte_memcpy(ð_hdr->dst_addr, &default_dst_mac, sizeof(struct rte_ether_addr)); + } + else + { + rte_memcpy(ð_hdr->dst_addr, dst_mac, sizeof(struct rte_ether_addr)); + } + + eth_hdr->ether_type = rte_cpu_to_be_16(eth_type); +} + +/** + * @brief Construct an IPv4 header + * + * @param ipv4_hdr Pointer to the IPv4 header + * @param src_ip Source IP address + * @param dst_ip Destination IP address + * @param proto Protocol + * @return int 0 on success, -1 on failure + */ +int ipv4_header_construct(struct rte_ipv4_hdr * ipv4_hdr, const char * src_ip, const char * dst_ip, uint8_t proto) +{ + const char * default_src_ip = "192.168.1.1"; + const char * default_dst_ip = "192.168.1.2"; + + if (src_ip == NULL) + { + src_ip = default_src_ip; + } + + if (dst_ip == NULL) + { + dst_ip = default_dst_ip; + } + + memset(ipv4_hdr, 0, sizeof(struct rte_ipv4_hdr)); + + ipv4_hdr->version_ihl = (4 << 4) | (sizeof(struct rte_ipv4_hdr) / 4); + ipv4_hdr->type_of_service = 0; + ipv4_hdr->total_length = rte_cpu_to_be_16(sizeof(struct rte_ipv4_hdr)); + ipv4_hdr->packet_id = 0; + ipv4_hdr->fragment_offset = rte_cpu_to_be_16(0x4000); // Don't fragment flag + ipv4_hdr->time_to_live = 64; + ipv4_hdr->next_proto_id = proto; + ipv4_hdr->hdr_checksum = 0; + + if (inet_pton(AF_INET, src_ip, &ipv4_hdr->src_addr) != 1) + { + return -1; + } + + if (inet_pton(AF_INET, dst_ip, &ipv4_hdr->dst_addr) != 1) + { + return -1; + } + + ipv4_hdr->hdr_checksum = rte_ipv4_cksum(ipv4_hdr); + return 0; +} + +/** + * @brief Construct a UDP header + * + * @param udp_hdr Pointer to the UDP header + * @param src_port Source port + * @param dst_port Destination port + * @param length Length of the UDP payload + */ +void udp_header_construct(struct rte_udp_hdr * udp_hdr, uint16_t src_port, uint16_t dst_port, uint16_t length) +{ + udp_hdr->src_port = rte_cpu_to_be_16(src_port); + udp_hdr->dst_port = rte_cpu_to_be_16(dst_port); + udp_hdr->dgram_len = sizeof(struct rte_udp_hdr) + length; + udp_hdr->dgram_cksum = 0; +} + +/** + * @brief Construct a BFD header + * + * @param bfd_hdr Pointer to the BFD header + * @param my_discriminator My discriminator + * @param your_discriminator Your discriminator + * @param state BFD state + */ +void bfd_header_construct(struct bfd_hdr * bfd_hdr, uint32_t * my_discriminator, uint32_t * your_discriminator, + uint8_t state) +{ + memset(bfd_hdr, 0, sizeof(struct bfd_hdr)); + + bfd_hdr->version_diag = 1; + bfd_hdr->flags = 0; + bfd_hdr->state = state; + + uint32_t discriminator = 0; + if (my_discriminator != NULL) + { + discriminator = rte_be_to_cpu_32(*my_discriminator); + rte_memcpy(bfd_hdr->my_discriminator, &discriminator, 4); + } + + if (your_discriminator != NULL) + { + discriminator = rte_be_to_cpu_32(*your_discriminator); + rte_memcpy(bfd_hdr->your_discriminator, your_discriminator, 4); + } +} + +/** + * @brief Creates a configuration file with the specified content. + * + * This function removes any existing file with the name "cmocka_test_config.ini", + * creates a new file with the same name, writes the provided content into the file, + * and then closes the file. If the file cannot be opened for writing, the function + * returns NULL. + * + * @param content A string containing the content to be written to the configuration file. + * + * @return The name of the created configuration file ("cmocka_test_config.ini") on success, + * or NULL if the file could not be created or written. + */ +const char * create_config_file(const char * content) +{ + const char * filename = "cmocka_test_config.ini"; + + remove(filename); + + FILE * fp_cfgfile = fopen(filename, "w"); + if (fp_cfgfile == NULL) + { + return NULL; + } + + fprintf(fp_cfgfile, "%s", content); + fclose(fp_cfgfile); + return filename; +} diff --git a/service/test/test_node_bfd.c b/service/test/test_node_bfd.c new file mode 100644 index 0000000..d428a00 --- /dev/null +++ b/service/test/test_node_bfd.c @@ -0,0 +1,970 @@ +#include <setjmp.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include <cmocka.h> + +#include <rte_eal.h> +#include <rte_graph.h> +#include <rte_graph_worker.h> +#include <rte_mempool.h> + +#include <sc_node.h> +#include <sc_node_common.h> + +struct bfd_node_main; +struct bfd_session; +struct bfd_session_handle; + +struct bfd_test_main +{ + struct sc_main * mock_sc; + rte_graph_t graph; + struct bfd_node_main * bfd_node_main; +}; + +extern int bfd_init(struct sc_main * sc); +extern void bfd_deinit(struct sc_main * sc); + +extern int bfd_node_main_delete(struct bfd_node_main * bfd_main); +extern struct bfd_node_main * bfd_node_main_create(uint16_t nr_graphs); +extern struct bfd_node_main * g_bfd_node_main_get(void); +extern int g_bfd_node_main_set(struct bfd_node_main * bfd_node_main); + +extern struct bfd_session_handle * bfd_session_handle_create(uint16_t max_sessions); +extern int bfd_session_handle_delete(struct bfd_session_handle * bfd_session_handle); +extern struct bfd_session_handle * bfd_session_handle_get(struct bfd_node_main * bfd_main, uint16_t graph_id); +extern int bfd_session_handle_is_empty(struct bfd_session_handle * bfd_session_handle); +extern int bfd_session_handle_is_full(struct bfd_session_handle * bfd_session_handle); +extern struct bfd_session * bfd_session_lookup(struct bfd_session_handle * bfd_session_handle, uint32_t ipv4_address); +extern struct bfd_session * bfd_session_add(struct bfd_session_handle * bfd_session_handle, uint32_t ipv4_address); +extern int bfd_session_update(struct bfd_session * bfd_session); +extern int bfd_session_delete(struct bfd_session_handle * bfd_session_handle, uint32_t ipv4_address); + +extern void eth_header_construct(struct rte_ether_hdr * eth_hdr, struct rte_ether_addr * src_mac, + struct rte_ether_addr * dst_mac, uint16_t eth_type); +extern int ipv4_header_construct(struct rte_ipv4_hdr * ipv4_hdr, const char * src_ip, const char * dst_ip, + uint8_t proto); +extern void udp_header_construct(struct rte_udp_hdr * udp_hdr, uint16_t src_port, uint16_t dst_port, uint16_t length); +extern void bfd_header_construct(struct bfd_hdr * bfd_hdr, uint32_t * my_discriminator, uint32_t * your_discriminator, + uint8_t state); +extern const char * create_config_file(const char * content); + +static struct rte_mempool * mock_pktmbuf_pool = NULL; + +/* ------------------------------------------- Test group setup/teardown ------------------------------------------- */ +static int testgroup_bfd_init_setup(void ** state) +{ + struct sc_main * mock_sc = rte_zmalloc(NULL, sizeof(struct sc_main), 0); + assert_non_null(mock_sc); + + const char * test_cfgfile_content = "[bfd]\n" + "disc_method=0"; + const char * filename = create_config_file(test_cfgfile_content); + assert_non_null(filename); + + strcpy(mock_sc->local_cfgfile, filename); + + mock_sc->nr_io_thread = 1; + *state = mock_sc; + return 0; +} + +static int testgroup_bfd_init_teardown(void ** state) +{ + struct sc_main * mock_sc = *state; + rte_free(mock_sc); + return 0; +} + +static int testgroup_bfd_discriminator_method_rfc_setup(void ** state) +{ + /* mock pkt source to generate mock packets */ + const char * node_patterns[] = { + "mock_pkt_input", + "mock_pkt_output", + "bfd", + }; + + /* create a fake graph */ + struct rte_graph_param graph_param = { + .node_patterns = node_patterns, + .nb_node_patterns = RTE_DIM(node_patterns), + .socket_id = 0, + }; + + /* update the pkt input's next id */ + rte_node_t node_id_pkt_input = rte_node_from_name("mock_pkt_input"); + assert_int_not_equal(node_id_pkt_input, RTE_NODE_ID_INVALID); + + const char * updated_next_node[] = {"bfd"}; + int rc = rte_node_edge_update(node_id_pkt_input, 0, updated_next_node, RTE_DIM(updated_next_node)); + assert_int_equal(rc, 1); + + /* update the node edge */ + rte_node_t node_id_bfd = rte_node_from_name("bfd"); + assert_int_not_equal(node_id_bfd, RTE_NODE_ID_INVALID); + + const char * bfd_updated_next_node[] = { + "mock_pkt_drop", + "mock_pkt_output", + }; + + rc = rte_node_edge_update(node_id_bfd, 0, bfd_updated_next_node, RTE_DIM(bfd_updated_next_node)); + + assert_int_equal(rc, 2); + + rte_graph_t graph = rte_graph_create("test_bfd", &graph_param); + assert_int_not_equal(graph, RTE_GRAPH_ID_INVALID); + + struct sc_main * mock_sc = rte_zmalloc(NULL, sizeof(struct sc_main), 0); + assert_non_null(mock_sc); + + mock_sc->nr_io_thread = 1; + + const char * test_cfgfile_content = "[bfd]\n" + "disc_method=0"; + const char * filename = create_config_file(test_cfgfile_content); + assert_non_null(filename); + + strcpy(mock_sc->local_cfgfile, filename); + + int ret = bfd_init(mock_sc); + assert_int_equal(ret, RT_SUCCESS); + + struct bfd_node_main * bfd_node_main = g_bfd_node_main_get(); + assert_non_null(bfd_node_main); + + struct bfd_test_main * bfd_test_main = rte_zmalloc(NULL, sizeof(struct bfd_test_main), 0); + assert_non_null(bfd_test_main); + + bfd_test_main->mock_sc = mock_sc; + bfd_test_main->graph = graph; + bfd_test_main->bfd_node_main = bfd_node_main; + + *state = bfd_test_main; + return 0; +} + +static int testgroup_bfd_discriminator_method_rfc_teardown(void ** state) +{ + struct bfd_test_main * bfd_test_main = *state; + + /* deinitialize the bfd */ + bfd_deinit(bfd_test_main->mock_sc); + assert_ptr_equal(g_bfd_node_main_get(), NULL); + + /* free the mock_sc */ + rte_free(bfd_test_main->mock_sc); + + /* destroy the graph */ + return rte_graph_destroy(bfd_test_main->graph); +} + +static int testgroup_bfd_discriminator_method_flipping_setup(void ** state) +{ + /* mock pkt source to generate mock packets */ + const char * node_patterns[] = { + "mock_pkt_input", + "mock_pkt_output", + "bfd", + }; + + /* create a fake graph */ + struct rte_graph_param graph_param = { + .node_patterns = node_patterns, + .nb_node_patterns = RTE_DIM(node_patterns), + .socket_id = 0, + }; + + /* update the pkt input's next id */ + rte_node_t node_id_pkt_input = rte_node_from_name("mock_pkt_input"); + assert_int_not_equal(node_id_pkt_input, RTE_NODE_ID_INVALID); + + const char * updated_next_node[] = {"bfd"}; + int rc = rte_node_edge_update(node_id_pkt_input, 0, updated_next_node, RTE_DIM(updated_next_node)); + assert_int_equal(rc, 1); + + /* update the node edge */ + rte_node_t node_id_bfd = rte_node_from_name("bfd"); + assert_int_not_equal(node_id_bfd, RTE_NODE_ID_INVALID); + + const char * bfd_updated_next_node[] = { + "mock_pkt_drop", + "mock_pkt_output", + }; + + rc = rte_node_edge_update(node_id_bfd, 0, bfd_updated_next_node, RTE_DIM(bfd_updated_next_node)); + + assert_int_equal(rc, 2); + + rte_graph_t graph = rte_graph_create("test_bfd", &graph_param); + assert_int_not_equal(graph, RTE_GRAPH_ID_INVALID); + + struct sc_main * mock_sc = rte_zmalloc(NULL, sizeof(struct sc_main), 0); + assert_non_null(mock_sc); + + mock_sc->nr_io_thread = 1; + + const char * test_cfgfile_content = "[bfd]\n" + "disc_method=1"; + const char * filename = create_config_file(test_cfgfile_content); + assert_non_null(filename); + + strcpy(mock_sc->local_cfgfile, filename); + + int ret = bfd_init(mock_sc); + assert_int_equal(ret, RT_SUCCESS); + + struct bfd_node_main * bfd_node_main = g_bfd_node_main_get(); + assert_non_null(bfd_node_main); + + struct bfd_test_main * bfd_test_main = rte_zmalloc(NULL, sizeof(struct bfd_test_main), 0); + assert_non_null(bfd_test_main); + + bfd_test_main->mock_sc = mock_sc; + bfd_test_main->graph = graph; + bfd_test_main->bfd_node_main = bfd_node_main; + + *state = bfd_test_main; + return 0; +} + +static int testgroup_bfd_discriminator_method_flipping_teardown(void ** state) +{ + struct bfd_test_main * bfd_test_main = *state; + + /* deinitialize the bfd */ + bfd_deinit(bfd_test_main->mock_sc); + assert_ptr_equal(g_bfd_node_main_get(), NULL); + + /* free the mock_sc */ + rte_free(bfd_test_main->mock_sc); + + /* destroy the graph */ + return rte_graph_destroy(bfd_test_main->graph); +} + +/* --------------------------------------------------- Test case --------------------------------------------------- */ +/* Test Case: BFD initialization success */ +static void testcase_bfd_init_success(void ** state) +{ + struct sc_main * mock_sc = *state; + + int rc = bfd_init(mock_sc); + assert_int_equal(rc, RT_SUCCESS); +} + +/* Test Case: BFD Deinitialize success */ +static void testcase_bfd_deinit_success(void ** state) +{ + struct sc_main * mock_sc = *state; + + bfd_deinit(mock_sc); + assert_ptr_equal(g_bfd_node_main_get(), NULL); +} + +/* Test Case: BFD node main API in a successful scenario */ +static void testcase_bfd_node_main_api_success(void ** state) +{ + struct bfd_node_main * bfd_node_main = bfd_node_main_create(RTE_MAX_LCORE); + assert_non_null(bfd_node_main); + + g_bfd_node_main_set(bfd_node_main); + struct bfd_node_main * bfd_node_main_get = g_bfd_node_main_get(); + assert_non_null(bfd_node_main_get); + + int ret = bfd_node_main_delete(bfd_node_main); + assert_int_equal(ret, RT_SUCCESS); + + g_bfd_node_main_set(NULL); + bfd_node_main_get = g_bfd_node_main_get(); + assert_ptr_equal(bfd_node_main_get, NULL); +} + +/* Test Case: BFD node main API in a failure scenario */ +static void testcase_bfd_node_main_api_failure(void ** state) +{ + struct bfd_node_main * bfd_node_main = bfd_node_main_create(0); + assert_null(bfd_node_main); + + int ret = bfd_node_main_delete(bfd_node_main); + assert_int_equal(ret, RT_ERR); + + ret = g_bfd_node_main_set(bfd_node_main); + assert_int_equal(ret, RT_SUCCESS); + + struct bfd_node_main * bfd_node_main_get = g_bfd_node_main_get(); + assert_ptr_equal(bfd_node_main_get, NULL); +} + +/* Test Case: BFD session handle API in a successful scenario */ +static void testcase_bfd_session_handle_api_success(void ** state) +{ + /* create the bfd session handle */ + struct bfd_session_handle * bfd_session_handle = bfd_session_handle_create(UINT16_MAX); + assert_non_null(bfd_session_handle); + + /* check the session handle is empty */ + int ret = bfd_session_handle_is_empty(bfd_session_handle); + assert_int_equal(ret, 1); + + /* add the bfd session */ + for (uint32_t i = 0; i < UINT16_MAX; i++) + { + struct bfd_session * bfd_session = bfd_session_add(bfd_session_handle, i); + assert_non_null(bfd_session); + } + + /* check the session handle is full */ + ret = bfd_session_handle_is_empty(bfd_session_handle); + assert_int_equal(ret, 0); + + ret = bfd_session_handle_is_full(bfd_session_handle); + assert_int_equal(ret, 1); + + /* lookup and update the bfd session */ + for (uint32_t i = 0; i < UINT16_MAX; i++) + { + struct bfd_session * bfd_session = bfd_session_lookup(bfd_session_handle, i); + assert_non_null(bfd_session); + + ret = bfd_session_update(bfd_session); + assert_int_equal(ret, RT_SUCCESS); + } + + /* delete the bfd session */ + for (uint32_t i = 0; i < UINT16_MAX; i++) + { + ret = bfd_session_delete(bfd_session_handle, i); + assert_int_equal(ret, RT_SUCCESS); + } + + /* check the session handle is empty */ + ret = bfd_session_handle_is_empty(bfd_session_handle); + assert_int_equal(ret, 1); + + /* delete the bfd session handle */ + ret = bfd_session_handle_delete(bfd_session_handle); + assert_int_equal(ret, RT_SUCCESS); +} + +/* Test Case: BFD session handle API in a failure scenario */ +static void testcase_bfd_session_handle_api_failure(void ** state) +{ + /* create the bfd session handle */ + struct bfd_session_handle * bfd_session_handle = bfd_session_handle_create(0); + assert_null(bfd_session_handle); + + /* delete the bfd session handle */ + int ret = bfd_session_handle_delete(bfd_session_handle); + assert_int_equal(ret, RT_ERR); + + bfd_session_handle = bfd_session_handle_create(1); + assert_non_null(bfd_session_handle); + + /* add the bfd session */ + struct bfd_session * bfd_session = bfd_session_add(bfd_session_handle, 1); + assert_non_null(bfd_session); + + /* add the bfd session again */ + bfd_session = bfd_session_add(bfd_session_handle, 2); + assert_null(bfd_session); + + /* delete the bfd session */ + ret = bfd_session_delete(bfd_session_handle, 2); + assert_int_equal(ret, RT_ERR); + + /* delete the bfd session again */ + ret = bfd_session_delete(bfd_session_handle, 1); + assert_int_equal(ret, RT_SUCCESS); + + /* lookup the bfd session */ + bfd_session = bfd_session_lookup(bfd_session_handle, 1); + assert_null(bfd_session); + + /* delete the bfd session handle */ + ret = bfd_session_handle_delete(bfd_session_handle); + assert_int_equal(ret, RT_SUCCESS); +} + +/* Test Case: rfc 5881 */ +/* + * BFD State Interaction Diagram + * + * A B + * -------- -------- + * DOWN DOWN + * | | + * | down->init | + * |-----------------------------------| + * | | + * | | + * | BFD DOWN | + * |-------------------------> | + * | | + * | | + * | BFD INIT | + * |<------------------------- | + * | | + * | | + * | BFD UP | + * |-------------------------> | + * | | + * | | + * | BFD UP | + * |<------------------------- | + * UP UP + * + * Legend: + * - BFD DOWN: BFD Down state packet + * - BFD INIT: BFD Init state packet + * - BFD UP: BFD Up state packet + * - XX->YY: State change from XX to YY + */ +static void testcase_bfd_rfc5881(void ** state) +{ + struct rte_graph * graph_ptr = rte_graph_lookup("test_bfd"); + assert_non_null(graph_ptr); + + /* alloc the mbuf as input */ + struct rte_mbuf * mbuf_input[1]; + mbuf_input[0] = rte_pktmbuf_alloc(mock_pktmbuf_pool); + + assert_non_null(mbuf_input); + + /* set the return value for the mock function */ + will_return(mock_pkt_input_process, 1); + will_return(mock_pkt_input_process, mbuf_input); + + struct rte_mbuf * mbuf_output[1] = {}; + unsigned int nr_mbuf_output = 0; + + will_return(mock_pkt_output_process, mbuf_output); + will_return(mock_pkt_output_process, &nr_mbuf_output); + + /* set mbuf pkt_len */ + mbuf_input[0]->data_len = sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_udp_hdr) + + sizeof(struct bfd_hdr); + mbuf_input[0]->pkt_len = mbuf_input[0]->data_len; + + /* prepare the bfd packet for initialization */ + struct rte_ether_hdr * ether_hdr = rte_pktmbuf_mtod(mbuf_input[0], struct rte_ether_hdr *); + struct rte_ipv4_hdr * ipv4_hdr = (struct rte_ipv4_hdr *)(ether_hdr + 1); + struct rte_udp_hdr * udp_hdr = (struct rte_udp_hdr *)(ipv4_hdr + 1); + struct bfd_hdr * bfd_hdr = (struct bfd_hdr *)(udp_hdr + 1); + + /* + * 1. bfd down to init + */ + + /* set the ether header */ + struct rte_ether_addr dst_mac = {.addr_bytes = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66}}; + struct rte_ether_addr src_mac = {.addr_bytes = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}}; + eth_header_construct(ether_hdr, &src_mac, &dst_mac, RTE_ETHER_TYPE_IPV4); + + /* set the ipv4 header */ + ipv4_header_construct(ipv4_hdr, NULL, NULL, IPPROTO_UDP); + + /* set the udp header */ + udp_header_construct(udp_hdr, 3784, 3784, sizeof(struct bfd_hdr)); + + /* set the bfd header */ + uint32_t discriminator_local = 0x12345678; + bfd_header_construct(bfd_hdr, &discriminator_local, NULL, BFD_STATE_DOWN); + + struct mrb_metadata * mrb_meta = mrbuf_cz_data(mbuf_input[0], MR_NODE_CTRLZONE_ID); + mrb_metadata_clear(mrb_meta); + mrb_meta->port_ingress = 1; + + /* prepare to parse the ingress pkt */ + struct pkt_parser pkt_parser; + pkt_parser_init(&pkt_parser, &mrb_meta->pkt_parser_result, LAYER_TYPE_ALL, MR_PKT_PARSER_LAYERS_MAX); + pkt_parser_exec(&pkt_parser, mbuf_input[0]); + + rte_graph_walk(graph_ptr); + + /* check the count */ + assert_int_equal(nr_mbuf_output, 1); + + /* check the mbuf ptr */ + assert_ptr_equal(mbuf_output[0], mbuf_input[0]); + + /* check the port egress */ + struct mrb_metadata * mrb_meta_output = mrbuf_cz_data(mbuf_output[0], MR_NODE_CTRLZONE_ID); + assert_int_equal(mrb_meta_output->port_egress, 1); + + /* check the eth mac addr */ + struct rte_ether_hdr * eth_hdr_output = rte_pktmbuf_mtod(mbuf_output[0], struct rte_ether_hdr *); + assert_memory_equal(ð_hdr_output->dst_addr, &src_mac, sizeof(struct rte_ether_addr)); + assert_memory_equal(ð_hdr_output->src_addr, &dst_mac, sizeof(struct rte_ether_addr)); + + /* check the ipv4 header */ + uint32_t src_ip, dst_ip; + inet_pton(AF_INET, "192.168.1.2", &src_ip); + inet_pton(AF_INET, "192.168.1.1", &dst_ip); + + struct rte_ipv4_hdr * ipv4_hdr_output = (struct rte_ipv4_hdr *)(eth_hdr_output + 1); + assert_int_equal(ipv4_hdr_output->src_addr, src_ip); + assert_int_equal(ipv4_hdr_output->dst_addr, dst_ip); + assert_int_equal(ipv4_hdr_output->time_to_live, 255); + + uint16_t output_checksum = ipv4_hdr_output->hdr_checksum; + ipv4_hdr_output->hdr_checksum = 0; + assert_int_equal(output_checksum, rte_ipv4_cksum(ipv4_hdr_output)); + + /* check the udp header */ + struct rte_udp_hdr * udp_hdr_output = (struct rte_udp_hdr *)(ipv4_hdr_output + 1); + assert_int_equal(udp_hdr_output->src_port, rte_cpu_to_be_16(3784)); + assert_int_equal(udp_hdr_output->dst_port, rte_cpu_to_be_16(3784)); + + /* check the bfd header */ + uint32_t discriminator_checker = 0, discriminator_remote = 0; + struct bfd_hdr * bfd_hdr_output = (struct bfd_hdr *)(udp_hdr_output + 1); + + rte_memcpy(&discriminator_checker, bfd_hdr_output->your_discriminator, sizeof(uint32_t)); + assert_int_equal(discriminator_checker, rte_cpu_to_be_32(discriminator_local)); + + rte_memcpy(&discriminator_checker, bfd_hdr_output->my_discriminator, sizeof(uint32_t)); + assert_ptr_not_equal(discriminator_checker, 0); + discriminator_remote = discriminator_checker; + + assert_int_equal(bfd_hdr_output->state, BFD_STATE_INIT); + + /* + * 2. bfd init to up + */ + + will_return(mock_pkt_input_process, 1); + will_return(mock_pkt_input_process, mbuf_input); + + will_return(mock_pkt_output_process, mbuf_output); + will_return(mock_pkt_output_process, &nr_mbuf_output); + + /* swap the eth mac addr */ + struct rte_ether_addr swap_addr; + rte_ether_addr_copy(ð_hdr_output->dst_addr, &swap_addr); + rte_ether_addr_copy(ð_hdr_output->src_addr, ð_hdr_output->dst_addr); + rte_ether_addr_copy(&swap_addr, ð_hdr_output->src_addr); + + /* swap ipv4_hdr */ + uint32_t swap_ip = ipv4_hdr_output->src_addr; + ipv4_hdr_output->src_addr = ipv4_hdr_output->dst_addr; + ipv4_hdr_output->dst_addr = swap_ip; + ipv4_hdr_output->time_to_live = 255; + ipv4_hdr_output->hdr_checksum = rte_ipv4_cksum(ipv4_hdr_output); + + /* swap bfd discriminator */ + uint32_t discriminator_swap = 0; + rte_memcpy(&discriminator_swap, bfd_hdr_output->my_discriminator, sizeof(uint32_t)); + rte_memcpy(bfd_hdr_output->my_discriminator, &bfd_hdr_output->your_discriminator, sizeof(uint32_t)); + rte_memcpy(&bfd_hdr_output->your_discriminator, &discriminator_swap, sizeof(uint32_t)); + bfd_hdr_output->state = BFD_STATE_UP; + + rte_graph_walk(graph_ptr); + + /* check the count */ + assert_int_equal(nr_mbuf_output, 1); + + /* check the mbuf ptr */ + assert_ptr_equal(mbuf_output[0], mbuf_input[0]); + + /* check the port egress */ + mrb_meta_output = mrbuf_cz_data(mbuf_output[0], MR_NODE_CTRLZONE_ID); + assert_int_equal(mrb_meta_output->port_egress, 1); + + /* check the eth mac addr */ + assert_memory_equal(ð_hdr_output->dst_addr, &src_mac, sizeof(struct rte_ether_addr)); + assert_memory_equal(ð_hdr_output->src_addr, &dst_mac, sizeof(struct rte_ether_addr)); + + /* check the ipv4 header */ + assert_int_equal(ipv4_hdr_output->src_addr, src_ip); + assert_int_equal(ipv4_hdr_output->dst_addr, dst_ip); + assert_int_equal(ipv4_hdr_output->time_to_live, 255); + + output_checksum = ipv4_hdr_output->hdr_checksum; + ipv4_hdr_output->hdr_checksum = 0; + assert_int_equal(output_checksum, rte_ipv4_cksum(ipv4_hdr_output)); + + /* check the udp header */ + assert_int_equal(udp_hdr_output->src_port, rte_cpu_to_be_16(3784)); + assert_int_equal(udp_hdr_output->dst_port, rte_cpu_to_be_16(3784)); + + /* check the bfd header */ + rte_memcpy(&discriminator_checker, bfd_hdr_output->your_discriminator, sizeof(uint32_t)); + assert_int_equal(discriminator_checker, rte_cpu_to_be_32(discriminator_local)); + + rte_memcpy(&discriminator_checker, bfd_hdr_output->my_discriminator, sizeof(uint32_t)); + assert_int_equal(discriminator_checker, discriminator_remote); + + assert_int_equal(bfd_hdr_output->state, BFD_STATE_UP); + + /* release the mbufs */ + rte_pktmbuf_free_bulk(mbuf_output, 1); + + assert_true(rte_mempool_full(mock_pktmbuf_pool) == 1); +} + +/* Test Case: bfd session full */ +static void testcase_bfd_session_full(void ** state) +{ + struct rte_graph * graph_ptr = rte_graph_lookup("test_bfd"); + assert_non_null(graph_ptr); + + /* alloc the mbuf as input */ + struct rte_mbuf * mbuf_input[1]; + mbuf_input[0] = rte_pktmbuf_alloc(mock_pktmbuf_pool); + + assert_non_null(mbuf_input); + + /* set the return value for the mock function */ + will_return(mock_pkt_input_process, 1); + will_return(mock_pkt_input_process, mbuf_input); + + struct rte_mbuf * mbuf_output[1] = {}; + unsigned int nr_mbuf_output = 0; + + will_return(mock_pkt_output_process, mbuf_output); + will_return(mock_pkt_output_process, &nr_mbuf_output); + + struct rte_mbuf * mbuf_drop[1] = {}; + unsigned int nr_mbuf_drop = 0; + will_return(mock_pkt_drop_process, mbuf_drop); + will_return(mock_pkt_drop_process, &nr_mbuf_drop); + + /* set mbuf pkt_len */ + mbuf_input[0]->data_len = sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_udp_hdr) + + sizeof(struct bfd_hdr); + mbuf_input[0]->pkt_len = mbuf_input[0]->data_len; + + /* prepare the bfd packet for initialization */ + struct rte_ether_hdr * ether_hdr = rte_pktmbuf_mtod(mbuf_input[0], struct rte_ether_hdr *); + struct rte_ipv4_hdr * ipv4_hdr = (struct rte_ipv4_hdr *)(ether_hdr + 1); + struct rte_udp_hdr * udp_hdr = (struct rte_udp_hdr *)(ipv4_hdr + 1); + struct bfd_hdr * bfd_hdr = (struct bfd_hdr *)(udp_hdr + 1); + + /* set the ether header */ + eth_header_construct(ether_hdr, NULL, NULL, RTE_ETHER_TYPE_IPV4); + + /* set the ipv4 header */ + ipv4_header_construct(ipv4_hdr, "172.16.1.1", NULL, IPPROTO_UDP); + + /* set the udp header */ + udp_header_construct(udp_hdr, 3784, 3784, sizeof(struct bfd_hdr)); + + /* set the bfd header */ + uint32_t discriminator_local = 0x12345678; + bfd_header_construct(bfd_hdr, &discriminator_local, NULL, BFD_STATE_DOWN); + + /* set the metadata */ + struct mrb_metadata * mrb_meta = mrbuf_cz_data(mbuf_input[0], MR_NODE_CTRLZONE_ID); + mrb_metadata_clear(mrb_meta); + mrb_meta->port_ingress = 1; + + /* prepare to parse the ingress pkt */ + struct pkt_parser pkt_parser; + pkt_parser_init(&pkt_parser, &mrb_meta->pkt_parser_result, LAYER_TYPE_ALL, MR_PKT_PARSER_LAYERS_MAX); + pkt_parser_exec(&pkt_parser, mbuf_input[0]); + + /* simulate the scenario where the current BFD session handle is full */ + struct bfd_test_main * bfd_test_main = *state; + struct bfd_session_handle * bfd_session_handle = bfd_session_handle_get(bfd_test_main->bfd_node_main, 0); + assert_non_null(bfd_session_handle); + + while (bfd_session_handle_is_full(bfd_session_handle) == 0) + { + bfd_session_add(bfd_session_handle, 1); + } + + assert_int_equal(bfd_session_handle_is_full(bfd_session_handle), 1); + + /* run graph */ + rte_graph_walk(graph_ptr); + + /* check the count */ + assert_int_equal(nr_mbuf_drop, 1); + + /* check the mbuf ptr */ + assert_ptr_equal(mbuf_drop[0], mbuf_input[0]); + + /* release the mbufs */ + rte_pktmbuf_free_bulk(mbuf_drop, 1); + + assert_true(rte_mempool_full(mock_pktmbuf_pool) == 1); +} + +/* Test Case: discriminator method flipping */ +/* + * BFD State Interaction Diagram + * + * A B + * -------- -------- + * DOWN DOWN + * | | + * | down->init | + * |-----------------------------------| + * | | + * | | + * | BFD DOWN | + * |-------------------------> | + * | | + * | | + * | BFD INIT | + * |<------------------------- | + * | | + * | | + * | BFD UP | + * |-------------------------> | + * | | + * | | + * | BFD UP | + * |<------------------------- | + * UP UP + * + * Legend: + * - BFD DOWN: BFD Down state packet + * - BFD INIT: BFD Init state packet + * - BFD UP: BFD Up state packet + * - XX->YY: State change from XX to YY + */ +static void testcase_bfd_discriminator_method_flipping(void ** state) +{ + struct rte_graph * graph_ptr = rte_graph_lookup("test_bfd"); + assert_non_null(graph_ptr); + + /* alloc the mbuf as input */ + struct rte_mbuf * mbuf_input[1]; + mbuf_input[0] = rte_pktmbuf_alloc(mock_pktmbuf_pool); + + assert_non_null(mbuf_input); + + /* set the return value for the mock function */ + will_return(mock_pkt_input_process, 1); + will_return(mock_pkt_input_process, mbuf_input); + + struct rte_mbuf * mbuf_output[1] = {}; + unsigned int nr_mbuf_output = 0; + + will_return(mock_pkt_output_process, mbuf_output); + will_return(mock_pkt_output_process, &nr_mbuf_output); + + /* set mbuf pkt_len */ + mbuf_input[0]->data_len = sizeof(struct rte_ether_hdr) + sizeof(struct rte_ipv4_hdr) + sizeof(struct rte_udp_hdr) + + sizeof(struct bfd_hdr); + mbuf_input[0]->pkt_len = mbuf_input[0]->data_len; + + /* prepare the bfd packet for initialization */ + struct rte_ether_hdr * ether_hdr = rte_pktmbuf_mtod(mbuf_input[0], struct rte_ether_hdr *); + struct rte_ipv4_hdr * ipv4_hdr = (struct rte_ipv4_hdr *)(ether_hdr + 1); + struct rte_udp_hdr * udp_hdr = (struct rte_udp_hdr *)(ipv4_hdr + 1); + struct bfd_hdr * bfd_hdr = (struct bfd_hdr *)(udp_hdr + 1); + + /* + * 1. bfd down to init + */ + + /* set the ether header */ + struct rte_ether_addr dst_mac = {.addr_bytes = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66}}; + struct rte_ether_addr src_mac = {.addr_bytes = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}}; + eth_header_construct(ether_hdr, &src_mac, &dst_mac, RTE_ETHER_TYPE_IPV4); + + /* set the ipv4 header */ + ipv4_header_construct(ipv4_hdr, NULL, NULL, IPPROTO_UDP); + + /* set the udp header */ + udp_header_construct(udp_hdr, 3784, 3784, sizeof(struct bfd_hdr)); + + /* set the bfd header */ + uint32_t discriminator_local = 0x12345678, discriminator_remote = 0x87654321; + bfd_header_construct(bfd_hdr, &discriminator_local, &discriminator_remote, BFD_STATE_DOWN); + + struct mrb_metadata * mrb_meta = mrbuf_cz_data(mbuf_input[0], MR_NODE_CTRLZONE_ID); + mrb_metadata_clear(mrb_meta); + mrb_meta->port_ingress = 1; + + /* prepare to parse the ingress pkt */ + struct pkt_parser pkt_parser; + pkt_parser_init(&pkt_parser, &mrb_meta->pkt_parser_result, LAYER_TYPE_ALL, MR_PKT_PARSER_LAYERS_MAX); + pkt_parser_exec(&pkt_parser, mbuf_input[0]); + + rte_graph_walk(graph_ptr); + + /* check the count */ + assert_int_equal(nr_mbuf_output, 1); + + /* check the mbuf ptr */ + assert_ptr_equal(mbuf_output[0], mbuf_input[0]); + + /* check the port egress */ + struct mrb_metadata * mrb_meta_output = mrbuf_cz_data(mbuf_output[0], MR_NODE_CTRLZONE_ID); + assert_int_equal(mrb_meta_output->port_egress, 1); + + /* check the eth mac addr */ + struct rte_ether_hdr * eth_hdr_output = rte_pktmbuf_mtod(mbuf_output[0], struct rte_ether_hdr *); + assert_memory_equal(ð_hdr_output->dst_addr, &src_mac, sizeof(struct rte_ether_addr)); + assert_memory_equal(ð_hdr_output->src_addr, &dst_mac, sizeof(struct rte_ether_addr)); + + /* check the ipv4 header */ + uint32_t src_ip, dst_ip; + inet_pton(AF_INET, "192.168.1.2", &src_ip); + inet_pton(AF_INET, "192.168.1.1", &dst_ip); + + struct rte_ipv4_hdr * ipv4_hdr_output = (struct rte_ipv4_hdr *)(eth_hdr_output + 1); + assert_int_equal(ipv4_hdr_output->src_addr, src_ip); + assert_int_equal(ipv4_hdr_output->dst_addr, dst_ip); + assert_int_equal(ipv4_hdr_output->time_to_live, 255); + + uint16_t output_checksum = ipv4_hdr_output->hdr_checksum; + ipv4_hdr_output->hdr_checksum = 0; + assert_int_equal(output_checksum, rte_ipv4_cksum(ipv4_hdr_output)); + + /* check the udp header */ + struct rte_udp_hdr * udp_hdr_output = (struct rte_udp_hdr *)(ipv4_hdr_output + 1); + assert_int_equal(udp_hdr_output->src_port, rte_cpu_to_be_16(3784)); + assert_int_equal(udp_hdr_output->dst_port, rte_cpu_to_be_16(3784)); + + /* check the bfd header */ + uint32_t discriminator_checker = 0; + struct bfd_hdr * bfd_hdr_output = (struct bfd_hdr *)(udp_hdr_output + 1); + + rte_memcpy(&discriminator_checker, bfd_hdr_output->your_discriminator, sizeof(uint32_t)); + assert_int_equal(discriminator_checker, rte_cpu_to_be_32(discriminator_local)); + + rte_memcpy(&discriminator_checker, bfd_hdr_output->my_discriminator, sizeof(uint32_t)); + assert_ptr_not_equal(discriminator_checker, rte_cpu_to_be_32(discriminator_remote)); + + assert_int_equal(bfd_hdr_output->state, BFD_STATE_INIT); + + /* + * 2. bfd init to up + */ + + will_return(mock_pkt_input_process, 1); + will_return(mock_pkt_input_process, mbuf_input); + + will_return(mock_pkt_output_process, mbuf_output); + will_return(mock_pkt_output_process, &nr_mbuf_output); + + /* swap the eth mac addr */ + struct rte_ether_addr swap_addr; + rte_ether_addr_copy(ð_hdr_output->dst_addr, &swap_addr); + rte_ether_addr_copy(ð_hdr_output->src_addr, ð_hdr_output->dst_addr); + rte_ether_addr_copy(&swap_addr, ð_hdr_output->src_addr); + + /* swap ipv4_hdr */ + uint32_t swap_ip = ipv4_hdr_output->src_addr; + ipv4_hdr_output->src_addr = ipv4_hdr_output->dst_addr; + ipv4_hdr_output->dst_addr = swap_ip; + ipv4_hdr_output->time_to_live = 255; + ipv4_hdr_output->hdr_checksum = rte_ipv4_cksum(ipv4_hdr_output); + + /* swap bfd discriminator */ + uint32_t discriminator_swap = 0; + rte_memcpy(&discriminator_swap, bfd_hdr_output->my_discriminator, sizeof(uint32_t)); + rte_memcpy(bfd_hdr_output->my_discriminator, &bfd_hdr_output->your_discriminator, sizeof(uint32_t)); + rte_memcpy(&bfd_hdr_output->your_discriminator, &discriminator_swap, sizeof(uint32_t)); + bfd_hdr_output->state = BFD_STATE_UP; + + rte_graph_walk(graph_ptr); + + /* check the count */ + assert_int_equal(nr_mbuf_output, 1); + + /* check the mbuf ptr */ + assert_ptr_equal(mbuf_output[0], mbuf_input[0]); + + /* check the port egress */ + mrb_meta_output = mrbuf_cz_data(mbuf_output[0], MR_NODE_CTRLZONE_ID); + assert_int_equal(mrb_meta_output->port_egress, 1); + + /* check the eth mac addr */ + assert_memory_equal(ð_hdr_output->dst_addr, &src_mac, sizeof(struct rte_ether_addr)); + assert_memory_equal(ð_hdr_output->src_addr, &dst_mac, sizeof(struct rte_ether_addr)); + + /* check the ipv4 header */ + assert_int_equal(ipv4_hdr_output->src_addr, src_ip); + assert_int_equal(ipv4_hdr_output->dst_addr, dst_ip); + assert_int_equal(ipv4_hdr_output->time_to_live, 255); + + output_checksum = ipv4_hdr_output->hdr_checksum; + ipv4_hdr_output->hdr_checksum = 0; + assert_int_equal(output_checksum, rte_ipv4_cksum(ipv4_hdr_output)); + + /* check the udp header */ + assert_int_equal(udp_hdr_output->src_port, rte_cpu_to_be_16(3784)); + assert_int_equal(udp_hdr_output->dst_port, rte_cpu_to_be_16(3784)); + + /* check the bfd header */ + rte_memcpy(&discriminator_checker, bfd_hdr_output->your_discriminator, sizeof(uint32_t)); + assert_int_equal(discriminator_checker, rte_cpu_to_be_32(discriminator_local)); + + rte_memcpy(&discriminator_checker, bfd_hdr_output->my_discriminator, sizeof(uint32_t)); + assert_ptr_not_equal(discriminator_checker, rte_cpu_to_be_32(discriminator_remote)); + + assert_int_equal(bfd_hdr_output->state, BFD_STATE_UP); + + /* release the mbufs */ + rte_pktmbuf_free_bulk(mbuf_output, 1); + + assert_true(rte_mempool_full(mock_pktmbuf_pool) == 1); +} + +int main(int argc, char * argv[]) +{ + /* generate the eal args */ + static char * eal_args[] = { + "test_node_bfd", + "-c 0x1", + "--no-huge", + "--no-pci", + }; + + /* run the eal with no huge pages */ + int ret = rte_eal_init(RTE_DIM(eal_args), eal_args); + if (ret < 0) + { + rte_exit(EXIT_FAILURE, "Error with EAL initialization\n"); + } + + mock_pktmbuf_pool = rte_pktmbuf_pool_create("test_pktmbuf_pool", 1024, 32, 128, RTE_MBUF_DEFAULT_BUF_SIZE, + SOCKET_ID_ANY); + + const struct CMUnitTest group_bfd_node_main[] = { + cmocka_unit_test(testcase_bfd_node_main_api_success), + cmocka_unit_test(testcase_bfd_node_main_api_failure), + }; + + const struct CMUnitTest group_bfd_session_handle[] = { + cmocka_unit_test(testcase_bfd_session_handle_api_success), + cmocka_unit_test(testcase_bfd_session_handle_api_failure), + }; + + const struct CMUnitTest group_bfd_init[] = { + cmocka_unit_test(testcase_bfd_init_success), + cmocka_unit_test(testcase_bfd_deinit_success), + }; + + const struct CMUnitTest group_bfd_discriminator_method_rfc[] = { + + cmocka_unit_test(testcase_bfd_rfc5881), + cmocka_unit_test(testcase_bfd_session_full), + }; + + const struct CMUnitTest group_bfd_discriminator_method_flipping[] = { + + cmocka_unit_test(testcase_bfd_discriminator_method_flipping), + }; + + cmocka_run_group_tests(group_bfd_node_main, NULL, NULL); + cmocka_run_group_tests(group_bfd_session_handle, NULL, NULL); + cmocka_run_group_tests(group_bfd_init, testgroup_bfd_init_setup, testgroup_bfd_init_teardown); + cmocka_run_group_tests(group_bfd_discriminator_method_rfc, testgroup_bfd_discriminator_method_rfc_setup, + testgroup_bfd_discriminator_method_rfc_teardown); + cmocka_run_group_tests(group_bfd_discriminator_method_flipping, testgroup_bfd_discriminator_method_flipping_setup, + testgroup_bfd_discriminator_method_flipping_teardown); + + rte_eal_cleanup(); + return 0; +} diff --git a/service/test/test_sc_metrics.c b/service/test/test_sc_metrics.c new file mode 100644 index 0000000..b0e7d51 --- /dev/null +++ b/service/test/test_sc_metrics.c @@ -0,0 +1,128 @@ +#include <setjmp.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include <cmocka.h> + +#include <rte_eal.h> +#include <rte_mempool.h> + +#include <sc_metrics.h> +#include <sc_node.h> +#include <sc_node_common.h> + +/* Test Case: Sc metrics API in a successful scenario */ +static void testcase_sc_metrics_api_success(void ** state) +{ + /* create a new sc metrics handle */ + struct sc_metrics_handle * metrics_handle = sc_metrics_handle_create(7); + assert_non_null(metrics_handle); + + /* check the capacity of the metrics handle,To ensure cache line alignment, the capacity must be a multiple of 8. */ + assert_int_equal(metrics_handle->capacity % 8, 0); + + /* set the values for the metrics handle */ + uint64_t values[7] = {1, 2, 3, 4, 5, 6, 7}; + int ret = sc_metrics_values_set(metrics_handle, values, 7); + assert_int_equal(ret, RT_SUCCESS); + + /* get the values from the metrics handle */ + uint64_t get_values[7] = {0}; + ret = sc_metrics_values_get(metrics_handle, get_values, 7); + assert_int_equal(ret, RT_SUCCESS); + + /* check the values */ + for (int i = 0; i < 7; i++) + { + assert_int_equal(values[i], get_values[i]); + } + + /* accumulate the values */ + uint64_t inc_values[7] = {1, 2, 3, 4, 5, 6, 7}; + ret = sc_metrics_accumulate(metrics_handle, inc_values, 7); + assert_int_equal(ret, RT_SUCCESS); + + /* get the values from the metrics handle */ + ret = sc_metrics_values_get(metrics_handle, get_values, 7); + assert_int_equal(ret, RT_SUCCESS); + + /* check the values */ + for (int i = 0; i < 7; i++) + { + assert_int_equal(values[i] + inc_values[i], get_values[i]); + } + + /* Set the metric value with the key 0 */ + ret = sc_metrics_value_set(metrics_handle, 100, 0); + assert_int_equal(ret, RT_SUCCESS); + + /* get the value from the metrics handle */ + uint64_t get_value = 0; + ret = sc_metrics_value_get(metrics_handle, &get_value, 0); + assert_int_equal(ret, RT_SUCCESS); + + /* check the value */ + assert_int_equal(100, get_value); + + /* reset the values */ + ret = sc_metrics_values_reset(metrics_handle); + assert_int_equal(ret, RT_SUCCESS); + + /* get the values from the metrics handle */ + ret = sc_metrics_values_get(metrics_handle, get_values, 7); + assert_int_equal(ret, RT_SUCCESS); + + /* check the values */ + for (int i = 0; i < 7; i++) + { + assert_int_equal(0, get_values[i]); + } + + /* delete the metrics handle */ + ret = sc_metrics_handle_delete(metrics_handle); + assert_int_equal(ret, RT_SUCCESS); +} + +/* Test Case: Sc metrics API in a failure scenario */ +static void testcase_sc_metrics_api_failure(void ** state) +{ + /* create a new sc metrics handle */ + struct sc_metrics_handle * metrics_handle = sc_metrics_handle_create(0); + assert_null(metrics_handle); + + metrics_handle = sc_metrics_handle_create(SC_METRICS_MAX_CAPACITY + 1); + assert_null(metrics_handle); + + /* delete the metrics handle */ + int ret = sc_metrics_handle_delete(NULL); + assert_int_equal(ret, RT_ERR); +} + +int main(int argc, char * argv[]) +{ + /* generate the eal args */ + static char * eal_args[] = { + "test_node_bfd", + "-c 0x1", + "--no-huge", + "--no-pci", + }; + + /* run the eal with no huge pages */ + int ret = rte_eal_init(RTE_DIM(eal_args), eal_args); + if (ret < 0) + { + rte_exit(EXIT_FAILURE, "Error with EAL initialization\n"); + } + + const struct CMUnitTest group_sc_metrics[] = { + cmocka_unit_test(testcase_sc_metrics_api_success), + cmocka_unit_test(testcase_sc_metrics_api_failure), + }; + + cmocka_run_group_tests(group_sc_metrics, NULL, NULL); + rte_eal_cleanup(); + return 0; +} diff --git a/test/ptf_test/CMakeLists.txt b/test/ptf_test/CMakeLists.txt index a328237..30d45a5 100644 --- a/test/ptf_test/CMakeLists.txt +++ b/test/ptf_test/CMakeLists.txt @@ -55,7 +55,7 @@ add_test( NAME bfd_test COMMAND /usr/local/bin/ptf --test-dir ${CMAKE_CURRENT_SOURCE_DIR} --interface 0@veth0-ptf0 - bfd_test_for_etherfabric bfd_test_for_bfdd --test-params "source_dir='${CMAKE_BINARY_DIR}'" + bfd_test_for_etherfabric bfd_test_for_bfdd bfd_test_for_ttl --test-params "source_dir='${CMAKE_BINARY_DIR}'" ) # Add bridge test |
