#include #include #include #include #include #include #include #include #include #include /* 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 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, BFD_NEXT_ETH_EGRESS, BFD_NEXT_MAX }; /* BFD discriminator processing method */ enum bfd_discriminator_method { BFD_DISCRIMINATOR_METHOD_RFC_STANDARD = 0, // Based on RFC standard BFD_DISCRIMINATOR_METHOD_FLIPPING, // Flipping the discriminator BFD_DISCRIMINATOR_METHOD_MAX }; /* Bfd node main struct */ struct bfd_node_main { /* 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; }; /* 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) { return g_bfd_main; } /** * @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) { 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_node_main * bfd_node_main = bfd_node_main_create(sc->nr_io_thread); if (bfd_node_main == NULL) { 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; } /** * @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 NULL; } /** * @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) { assert(bfd_session_handle != NULL); if (unlikely(__bfd_session_handle_is_full(bfd_session_handle))) { MR_ERROR("Error adding bfd session: no free session id."); return 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 == 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; } /** * @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); } /** * @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; rte_ether_addr_copy(ðer_hdr->dst_addr, &swap_addr); rte_ether_addr_copy(ðer_hdr->src_addr, ðer_hdr->dst_addr); rte_ether_addr_copy(&swap_addr, ðer_hdr->src_addr); /* Swap ipv4_hdr */ uint32_t swap_ip = ipv4_hdr->dst_addr; ipv4_hdr->dst_addr = ipv4_hdr->src_addr; ipv4_hdr->src_addr = swap_ip; ipv4_hdr->time_to_live = 255; /* Recalculate the checksum */ ipv4_hdr->hdr_checksum = 0; ipv4_hdr->hdr_checksum = rte_ipv4_cksum(ipv4_hdr); /* Swap discriminator */ 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 */ uint8_t bfd_state = bfd_hdr->state; if (bfd_state == BFD_STATE_DOWN) { bfd_hdr->state = BFD_STATE_INIT; return bfd_state; } else if (bfd_state == BFD_STATE_INIT) { bfd_hdr->state = BFD_STATE_UP; return bfd_state; } bfd_hdr->state = BFD_STATE_UP; return 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]; int len = snprintf(str_record, sizeof(str_record), "next node:%s", node->nodes[next_node_index]->name); /* Populate the drop reason */ if (unlikely(next_node_index == BFD_NEXT_PKT_DROP)) { if (expect_result < 0) { len += snprintf(str_record + len, sizeof(str_record) - len, ", rsn:pkt noncompliant with bfd"); } else if (bfd_session == NULL) { len += snprintf(str_record + len, sizeof(str_record) - len, ", rsn:no free sessionid"); } } /* Populate the bfd state */ len += snprintf(str_record + len, sizeof(str_record) - len, ", bfd state:%u", bfd_state); /* Emit the trace record */ struct dp_trace_record_meta meta = { .measurement_type = DP_TRACE_MEASUREMENT_TYPE_TRACE, .appsym = MR_TRACE_APPSYM, .module = node->name}; dp_trace_record_emit_str(sc_main_get()->trace, mbuf, rte_lcore_id(), &meta, str_record); } /** * @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]; uint64_t unexpected_pkts[nr_graphs]; uint64_t no_free_session_id[nr_graphs]; for (uint32_t graph_id = 0; graph_id < nr_graphs; graph_id++) { 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; unexpected_pkts[graph_id] = 0; no_free_session_id[graph_id] = 0; continue; } 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); cJSON_AddItemToObject(json_root, "bfd, total_pkts", json_total_pkts); cJSON * json_pkts_per_batch = create_uint64_array(pkts_per_batch, nr_graphs); cJSON_AddItemToObject(json_root, "bfd, pkts_per_batch", json_pkts_per_batch); cJSON * json_unexpected_pkts = create_uint64_array(unexpected_pkts, nr_graphs); cJSON_AddItemToObject(json_root, "bfd, drop_for_unexpected_pkts", json_unexpected_pkts); cJSON * json_no_free_session_id = create_uint64_array(no_free_session_id, nr_graphs); cJSON_AddItemToObject(json_root, "bfd, drop_for_no_free_session_id", json_no_free_session_id); uint32_t session_num = 0; 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_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 < bfd_session_handle->max_sessions; ++i) { 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].ipv4_address != session->ipv4_address) continue; should_add = false; /* Update the session tsc */ if (session->prev_tsc > sessions[j].prev_tsc) { sessions[j].prev_tsc = session->prev_tsc; } break; } if (should_add && session_num < bfd_session_handle->max_sessions) { sessions[session_num].ipv4_address = session->ipv4_address; sessions[session_num].prev_tsc = session->prev_tsc; ++session_num; } } } for (int i = 0; i < session_num; ++i) { struct in_addr 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)); uint64_t prev_tsc = sessions[i].prev_tsc; uint64_t cur_tsc = rte_rdtsc(); uint64_t diff_tsc = cur_tsc - prev_tsc; if (unlikely(diff_tsc > drain_tsc)) cJSON_AddStringToObject(session_obj, "status", "Down"); else cJSON_AddStringToObject(session_obj, "status", "Up"); char session_index[MR_STRING_MAX] = {}; snprintf(session_index, sizeof(session_index), "session-%u", i); cJSON_AddItemToObject(json_root, session_index, session_obj); } 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) { uint8_t bfd_state = BFD_STATE_ADMINDOWN; struct bfd_session * bfd_session = NULL; 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 */ 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 */ 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);