/* * Copyright (c) 2022-2026 NVIDIA CORPORATION AND AFFILIATES. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted * provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include #include #include #include #include #include #include DOCA_LOG_REGISTER(FLOW_RSS_META); #define PACKET_BURST 128 /* The number of packets in the rx queue */ /* * Dequeue packets from DPDK queues * * @ingress_port [in]: port id for dequeue packets */ static void process_packets(int ingress_port) { struct rte_mbuf *packets[PACKET_BURST]; int queue_index = 0; int nb_packets; int i; nb_packets = rte_eth_rx_burst(ingress_port, queue_index, packets, PACKET_BURST); /* Print received packets meta data */ if (nb_packets) DOCA_LOG_INFO("Port %d: Received %d packets", ingress_port, nb_packets); for (i = 0; i < nb_packets; i++) { if (rte_flow_dynf_metadata_avail()) DOCA_LOG_INFO("Packet %d meta data %d", i, *RTE_FLOW_DYNF_METADATA(packets[i])); } } /* * Create DOCA Flow pipe with 5 tuple match, changeable set meta action, and forward RSS * * @port [in]: port of the pipe * @pipe [out]: created pipe pointer * @port_id [in]: port id * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise */ static doca_error_t create_rss_meta_pipe(struct doca_flow_port *port, struct doca_flow_pipe **pipe, int port_id) { struct doca_flow_match match; struct doca_flow_actions actions, *actions_arr[NB_ACTIONS_ARR]; struct doca_flow_fwd fwd; struct doca_flow_fwd fwd_miss; struct doca_flow_pipe_cfg *pipe_cfg; uint16_t rss_queues[1]; doca_error_t result; enum doca_rss_type ip_rss_flag = port_id == 0 ? DOCA_FLOW_RSS_IPV4_SRC : DOCA_FLOW_RSS_IPV4_DST; enum doca_rss_type tcp_rss_flag = port_id == 0 ? DOCA_FLOW_RSS_TCP_SRC : DOCA_FLOW_RSS_TCP_DST; memset(&match, 0, sizeof(match)); memset(&actions, 0, sizeof(actions)); memset(&fwd, 0, sizeof(fwd)); memset(&fwd_miss, 0, sizeof(fwd_miss)); /* set mask value */ actions.meta.pkt_meta = UINT32_MAX; actions_arr[0] = &actions; /* 5 tuple match */ match.parser_meta.outer_l4_type = DOCA_FLOW_L4_META_TCP; match.parser_meta.outer_l3_type = DOCA_FLOW_L3_META_IPV4; match.outer.l4_type_ext = DOCA_FLOW_L4_TYPE_EXT_TCP; match.outer.l3_type = DOCA_FLOW_L3_TYPE_IP4; match.outer.ip4.src_ip = 0xffffffff; match.outer.ip4.dst_ip = 0xffffffff; match.outer.tcp.l4_port.src_port = 0xffff; match.outer.tcp.l4_port.dst_port = 0xffff; result = doca_flow_pipe_cfg_create(&pipe_cfg, port); if (result != DOCA_SUCCESS) { DOCA_LOG_ERR("Failed to create doca_flow_pipe_cfg: %s", doca_error_get_descr(result)); return result; } result = set_flow_pipe_cfg(pipe_cfg, "RSS_META_PIPE", DOCA_FLOW_PIPE_BASIC, true); if (result != DOCA_SUCCESS) { DOCA_LOG_ERR("Failed to set doca_flow_pipe_cfg: %s", doca_error_get_descr(result)); goto destroy_pipe_cfg; } result = doca_flow_pipe_cfg_set_match(pipe_cfg, &match, NULL); if (result != DOCA_SUCCESS) { DOCA_LOG_ERR("Failed to set doca_flow_pipe_cfg match: %s", doca_error_get_descr(result)); goto destroy_pipe_cfg; } result = doca_flow_pipe_cfg_set_actions(pipe_cfg, actions_arr, NULL, NULL, NB_ACTIONS_ARR); if (result != DOCA_SUCCESS) { DOCA_LOG_ERR("Failed to set doca_flow_pipe_cfg actions: %s", doca_error_get_descr(result)); goto destroy_pipe_cfg; } /* RSS queue - send matched traffic to queue 0 */ rss_queues[0] = 0; fwd.type = DOCA_FLOW_FWD_RSS; fwd.rss_type = DOCA_FLOW_RESOURCE_TYPE_NON_SHARED; fwd.rss.queues_array = rss_queues; fwd.rss.inner_flags = ip_rss_flag | tcp_rss_flag; fwd.rss.nr_queues = 1; fwd_miss.type = DOCA_FLOW_FWD_DROP; result = doca_flow_pipe_create(pipe_cfg, &fwd, &fwd_miss, pipe); destroy_pipe_cfg: doca_flow_pipe_cfg_destroy(pipe_cfg); return result; } /* * Add DOCA Flow pipe entry with example 5 tuple to match and set meta data value * * @pipe [in]: pipe of the entry * @status [in]: user context for adding entry * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise */ static doca_error_t add_rss_meta_pipe_entry(struct doca_flow_pipe *pipe, struct entries_status *status) { struct doca_flow_match match; struct doca_flow_actions actions; struct doca_flow_pipe_entry *entry; doca_error_t result; /* example 5-tuple to drop */ doca_be32_t dst_ip_addr = BE_IPV4_ADDR(8, 8, 8, 8); doca_be32_t src_ip_addr = BE_IPV4_ADDR(1, 2, 3, 4); doca_be16_t dst_port = DOCA_HTOBE16(80); doca_be16_t src_port = DOCA_HTOBE16(1234); memset(&match, 0, sizeof(match)); memset(&actions, 0, sizeof(actions)); match.outer.ip4.dst_ip = dst_ip_addr; match.outer.ip4.src_ip = src_ip_addr; match.outer.tcp.l4_port.dst_port = dst_port; match.outer.tcp.l4_port.src_port = src_port; /* set meta value */ actions.meta.pkt_meta = DOCA_HTOBE32(10); result = doca_flow_pipe_basic_add_entry(0, pipe, &match, 0, &actions, NULL, NULL, 0, status, &entry); if (result != DOCA_SUCCESS) return result; return DOCA_SUCCESS; } /* Context structure for statistics printing */ struct rss_meta_stats_context { int nb_ports; }; /* * Print RSS meta statistics * * @nb_ports [in]: number of ports */ static void print_rss_meta_stats(int nb_ports) { int port_id; for (port_id = 0; port_id < nb_ports; port_id++) process_packets(port_id); } /* * Wrapper function for statistics printing compatible with flow_wait_for_packets * * @context [in]: rss_meta_stats_context structure */ static void print_rss_meta_stats_wrapper(void *context) { struct rss_meta_stats_context *ctx = (struct rss_meta_stats_context *)context; print_rss_meta_stats(ctx->nb_ports); } /* * Run flow_rss_meta sample * * @nb_queues [in]: number of queues the sample will use * @return: DOCA_SUCCESS on success and DOCA_ERROR otherwise */ doca_error_t flow_rss_meta(int nb_queues) { const int nb_ports = 2; struct flow_resources resource = {0}; uint32_t nr_shared_resources[SHARED_RESOURCE_NUM_VALUES] = {0}; struct doca_flow_port *ports[nb_ports]; uint32_t actions_mem_size[nb_ports]; struct doca_flow_pipe *pipe; struct entries_status status; int num_of_entries = 1; doca_error_t result; int port_id; result = init_doca_flow(nb_queues, "vnf,hws", &resource, nr_shared_resources); if (result != DOCA_SUCCESS) { DOCA_LOG_ERR("Failed to init DOCA Flow: %s", doca_error_get_descr(result)); return -1; } ARRAY_INIT(actions_mem_size, ACTIONS_MEM_SIZE(num_of_entries)); resource.mode = DOCA_FLOW_RESOURCE_MODE_PORT; resource.nr_rss = num_of_entries; result = init_doca_flow_vnf_ports(nb_ports, ports, actions_mem_size, &resource); if (result != DOCA_SUCCESS) { DOCA_LOG_ERR("Failed to init DOCA ports: %s", doca_error_get_descr(result)); doca_flow_destroy(); return result; } for (port_id = 0; port_id < nb_ports; port_id++) { memset(&status, 0, sizeof(status)); result = create_rss_meta_pipe(ports[port_id], &pipe, port_id); if (result != DOCA_SUCCESS) { DOCA_LOG_ERR("Failed to create pipe: %s", doca_error_get_descr(result)); stop_doca_flow_ports(nb_ports, ports); doca_flow_destroy(); return result; } result = add_rss_meta_pipe_entry(pipe, &status); if (result != DOCA_SUCCESS) { DOCA_LOG_ERR("Failed to add entry: %s", doca_error_get_descr(result)); stop_doca_flow_ports(nb_ports, ports); doca_flow_destroy(); return result; } result = doca_flow_pipe_queue_drain(ports[port_id], 0, NULL); if (result != DOCA_SUCCESS) { DOCA_LOG_ERR("Failed to process entries: %s", doca_error_get_descr(result)); stop_doca_flow_ports(nb_ports, ports); doca_flow_destroy(); return result; } if (status.nb_processed != num_of_entries || status.failure) { DOCA_LOG_ERR("Failed to process entries"); stop_doca_flow_ports(nb_ports, ports); doca_flow_destroy(); return DOCA_ERROR_BAD_STATE; } } DOCA_LOG_INFO("Wait few seconds for packets to arrive"); /* Setup statistics context and wait for packets */ struct rss_meta_stats_context stats_ctx = {.nb_ports = nb_ports}; flow_wait_for_packets(5, print_rss_meta_stats_wrapper, &stats_ctx); result = stop_doca_flow_ports(nb_ports, ports); doca_flow_destroy(); return result; }