NVIDIA DOCA SDK Data Center on a Chip Framework Documentation
psp_gateway.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2024-2025 NVIDIA CORPORATION AND AFFILIATES. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without modification, are permitted
5  * provided that the following conditions are met:
6  * * Redistributions of source code must retain the above copyright notice, this list of
7  * conditions and the following disclaimer.
8  * * Redistributions in binary form must reproduce the above copyright notice, this list of
9  * conditions and the following disclaimer in the documentation and/or other materials
10  * provided with the distribution.
11  * * Neither the name of the NVIDIA CORPORATION nor the names of its contributors may be used
12  * to endorse or promote products derived from this software without specific prior written
13  * permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
17  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
20  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
21  * STRICT LIABILITY, OR TOR (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  */
25 
26 // system headers
27 #include <fcntl.h>
28 #include <memory>
29 #include <signal.h>
30 
31 // DPDK
32 #include <rte_ethdev.h>
33 
34 // gRPC
35 #include <google/protobuf/util/json_util.h>
36 #include <grpc/support/log.h>
37 #include <grpcpp/grpcpp.h>
38 #include <grpcpp/server_builder.h>
39 
40 // DOCA
41 #include <dpdk_utils.h>
42 #include <doca_argp.h>
43 #include <doca_dev.h>
44 #include <doca_flow.h>
45 #include <doca_log.h>
46 #include <doca_dpdk.h>
47 #include <samples/common.h>
48 
49 // application
50 #include <psp_gw_config.h>
51 #include <psp_gw_flows.h>
52 #include <psp_gw_svc_impl.h>
53 #include <psp_gw_params.h>
54 #include <psp_gw_pkt_rss.h>
55 #include <psp_gw_utils.h>
56 
57 DOCA_LOG_REGISTER(PSP_GATEWAY);
58 
59 volatile bool force_quit; // Set when signal is received
60 
66 static void signal_handler(int signum)
67 {
68  if (signum == SIGINT || signum == SIGTERM) {
69  DOCA_LOG_INFO("Signal %d received, preparing to exit", signum);
70  force_quit = true;
71  }
72 }
73 
74 /*
75  * @brief PSP Gateway application main function
76  *
77  * @argc [in]: command line arguments size
78  * @argv [in]: array of command line arguments
79  * @return: EXIT_SUCCESS on success and EXIT_FAILURE otherwise
80  */
81 int main(int argc, char **argv)
82 {
84  int nb_ports = 2;
85  int exit_status = EXIT_SUCCESS;
86 
87  struct psp_gw_app_config app_config = {};
88  app_config.dpdk_config.port_config.nb_ports = nb_ports;
89  app_config.dpdk_config.port_config.nb_queues = 2;
90  app_config.dpdk_config.port_config.switch_mode = true;
91  app_config.dpdk_config.port_config.enable_mbuf_metadata = true;
92  app_config.dpdk_config.port_config.isolated_mode = true;
93  app_config.dpdk_config.reserve_main_thread = true;
94  app_config.pf_repr_indices = "[0]";
95  app_config.core_mask = "0x3";
96  app_config.max_tunnels = 256;
97  app_config.net_config.vc_enabled = false;
98  app_config.net_config.crypt_offset = UINT32_MAX;
99  app_config.net_config.default_psp_proto_ver = UINT32_MAX;
100  app_config.log2_sample_rate = 0;
101  app_config.ingress_sample_meta_indicator = 0x65656565; // arbitrary pkt_meta flag value
102  app_config.egress_sample_meta_indicator = 0x43434343;
103  app_config.return_to_vf_indicator = 0x78787878;
104  app_config.show_sampled_packets = true;
105  app_config.show_rss_rx_packets = false;
106  app_config.show_rss_durations = false;
110 
111  struct psp_pf_dev pf_dev = {};
112  uint16_t vf_port_id;
113  std::string dev_probe_str;
114 
115  struct doca_log_backend *sdk_log;
116 
117  // Register a logger backend
119  if (result != DOCA_SUCCESS)
120  return EXIT_FAILURE;
121 
122  // Register a logger backend for internal SDK errors and warnings
123  result = doca_log_backend_create_with_file_sdk(stdout, &sdk_log);
124  if (result != DOCA_SUCCESS)
125  return EXIT_FAILURE;
126 
128  if (result != DOCA_SUCCESS)
129  return EXIT_FAILURE;
130 
131  signal(SIGINT, signal_handler);
132  signal(SIGTERM, signal_handler);
133 
134  result = psp_gw_argp_exec(argc, argv, &app_config);
135  if (result != DOCA_SUCCESS) {
136  return EXIT_FAILURE;
137  }
138 
139  // init DPDK
140  std::string pid_str = "pid_" + std::to_string(getpid());
141  const char *eal_args[] = {"", "-a00:00.0", "-c", app_config.core_mask.c_str(), "--file-prefix", pid_str.c_str()};
142  int n_eal_args = sizeof(eal_args) / sizeof(eal_args[0]);
143  int rc = rte_eal_init(n_eal_args, (char **)eal_args);
144  if (rc < 0) {
145  DOCA_LOG_ERR("EAL initialization failed");
146  return DOCA_ERROR_DRIVER;
147  }
148 
150  if (result != DOCA_SUCCESS) {
151  exit_status = EXIT_FAILURE;
152  goto dpdk_destroy;
153  }
154 
155  if (app_config.net_config.crypt_offset == UINT32_MAX) {
156  // If not specified by argp, select a default crypt_offset
157  if (app_config.inner == DOCA_FLOW_L3_TYPE_IP4)
158  app_config.net_config.crypt_offset = app_config.net_config.vc_enabled ?
161  else
162  app_config.net_config.crypt_offset = app_config.net_config.vc_enabled ?
165  DOCA_LOG_INFO("Selected crypt_offset of %d", app_config.net_config.crypt_offset);
166  }
167 
168  if (app_config.net_config.default_psp_proto_ver == UINT32_MAX) {
169  // If not specified by argp, select a default PSP protocol version
170  app_config.net_config.default_psp_proto_ver = DEFAULT_PSP_VERSION;
171  DOCA_LOG_INFO("Selected psp_ver %d", app_config.net_config.default_psp_proto_ver);
172  }
173 
174  // init devices
175  result = open_doca_device_with_pci(app_config.pf_pcie_addr.c_str(), nullptr, &pf_dev.dev);
176  if (result != DOCA_SUCCESS) {
177  DOCA_LOG_ERR("Failed to open device %s: %s",
178  app_config.pf_pcie_addr.c_str(),
180  exit_status = EXIT_FAILURE;
181  goto dpdk_destroy;
182  }
183 
184  dev_probe_str = std::string("dv_flow_en=2," // hardware steering
185  "dv_xmeta_en=4," // extended flow metadata support
186  "fdb_def_rule_en=0," // disable default root flow table rule
187  "vport_match=1,"
188  "repr_matching_en=0,"
189  "representor=") +
190  app_config.pf_repr_indices; // indicate which representors to probe
191 
192  result = doca_dpdk_port_probe(pf_dev.dev, dev_probe_str.c_str());
193  if (result != DOCA_SUCCESS) {
194  DOCA_LOG_ERR("Failed to probe dpdk port for secured port: %s", doca_error_get_descr(result));
195  exit_status = EXIT_FAILURE;
196  goto dev_close;
197  }
198  DOCA_LOG_INFO("Probed %s,%s", app_config.pf_pcie_addr.c_str(), dev_probe_str.c_str());
199 
200  pf_dev.port_id = 0;
201 
202  app_config.dpdk_config.port_config.nb_ports = rte_eth_dev_count_avail();
203 
204  rte_eth_macaddr_get(pf_dev.port_id, &pf_dev.src_mac);
205  pf_dev.src_mac_str = mac_to_string(pf_dev.src_mac);
206 
207  if (app_config.outer == DOCA_FLOW_L3_TYPE_IP4) {
210  (uint8_t *)&pf_dev.src_pip.ipv4_addr,
212  if (result != DOCA_SUCCESS) {
213  DOCA_LOG_ERR("Failed to find IPv4 addr for PF: %s", doca_error_get_descr(result));
214  exit_status = EXIT_FAILURE;
215  goto dev_close;
216  }
217  } else {
220  (uint8_t *)pf_dev.src_pip.ipv6_addr,
222  if (result != DOCA_SUCCESS) {
223  DOCA_LOG_ERR("Failed to find IPv6 addr for PF: %s", doca_error_get_descr(result));
224  exit_status = EXIT_FAILURE;
225  goto dev_close;
226  }
227  }
228  pf_dev.src_pip_str = ip_to_string(pf_dev.src_pip);
229 
230  DOCA_LOG_INFO("Port %d: Detected PF MAC addr: %s, IP addr: %s, total ports: %d",
231  pf_dev.port_id,
232  pf_dev.src_mac_str.c_str(),
233  pf_dev.src_pip_str.c_str(),
234  app_config.dpdk_config.port_config.nb_ports);
235 
236  vf_port_id = pf_dev.port_id + 1;
237 
238  // Update queues and ports
240  if (result != DOCA_SUCCESS) {
241  DOCA_LOG_ERR("Failed to update application ports and queues: %s", doca_error_get_descr(result));
242  exit_status = EXIT_FAILURE;
243  goto dev_close;
244  }
245 
246  {
247  PSP_GatewayFlows psp_flows(&pf_dev, vf_port_id, &app_config);
248  std::vector<psp_gw_peer> remotes_to_connect;
249  uint32_t lcore_id;
250 
251  result = psp_flows.init();
252  if (result != DOCA_SUCCESS) {
253  DOCA_LOG_ERR("Failed to create flow pipes");
254  exit_status = EXIT_FAILURE;
255  goto dpdk_cleanup;
256  }
257 
258  PSP_GatewayImpl psp_svc(&app_config, &psp_flows);
259 
260  struct lcore_params lcore_params = {
261  &force_quit,
262  &app_config,
263  &pf_dev,
264  &psp_flows,
265  &psp_svc,
266  };
267 
268  RTE_LCORE_FOREACH_WORKER(lcore_id)
269  {
270  rte_eal_remote_launch(lcore_pkt_proc_func, &lcore_params, lcore_id);
271  }
272 
273  std::string server_address = app_config.local_svc_addr;
274  if (server_address.empty()) {
275  server_address = "0.0.0.0";
276  }
277  if (server_address.find(":") == std::string::npos) {
279  }
280  grpc::ServerBuilder builder;
281  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
282  builder.RegisterService(&psp_svc);
283  auto server_instance = builder.BuildAndStart();
284 
285  if (!server_instance) {
286  DOCA_LOG_ERR("Failed to initialize gRPC server (%s)", server_address.c_str());
287  exit_status = EXIT_FAILURE;
288  goto workers_cleanup;
289  } else {
290  DOCA_LOG_WARN("Server listening on %s", server_address.c_str());
291  }
292 
293  // If configured to create all tunnels at startup, create a list of
294  // pending tunnels here. Each invocation of try_connect will
295  // remove entries from the list as tunnels are created.
296  // Otherwise, this list will be left empty and tunnels will be created
297  // on demand via the miss path.
298 
299  if (app_config.create_tunnels_at_startup) {
300  remotes_to_connect = app_config.net_config.peers;
301  }
302 
303  while (!force_quit) {
304  psp_svc.try_connect(remotes_to_connect);
305  sleep(1);
306 
307  if (app_config.print_stats) {
310  }
311  }
312 
313  DOCA_LOG_INFO("Shutting down");
314 
315  if (server_instance) {
316  server_instance->Shutdown();
317  server_instance.reset();
318  }
319 
320 workers_cleanup:
321  force_quit = true;
322  RTE_LCORE_FOREACH_WORKER(lcore_id)
323  {
324  DOCA_LOG_INFO("Stopping L-Core %d", lcore_id);
325  rte_eal_wait_lcore(lcore_id);
326  }
327  }
328 
329 dpdk_cleanup:
331 dev_close:
333 dpdk_destroy:
334  dpdk_fini();
336 
337  return exit_status;
338 }
int32_t result
The entity which owns all the doca flow shared resources and flow pipes (but not sessions).
Definition: psp_gw_flows.h:87
void show_static_flow_counts(void)
Shows flow counters for pipes which have a fixed number of entries, if any counter values have change...
doca_error_t init(void)
Initialized the DOCA resources.
Implementation of the PSP_Gateway service.
doca_error_t show_flow_counts(void)
Displays the counters of all tunnel sessions that have changed since the previous invocation.
static constexpr uint16_t DEFAULT_HTTP_PORT_NUM
size_t try_connect(std::vector< psp_gw_peer > &peers)
Attempt to establish tunnels to each of the passed peers. On success, a given peer is removed from th...
static doca_error_t open_doca_device_with_pci(const char *pcie_value, struct doca_dev **retval)
Definition: device.c:43
void dpdk_fini(void)
Definition: dpdk_utils.c:919
doca_error_t dpdk_queues_and_ports_init(struct application_dpdk_config *app_dpdk_config)
Definition: dpdk_utils.c:515
void dpdk_queues_and_ports_fini(struct application_dpdk_config *app_dpdk_config)
Definition: dpdk_utils.c:564
DOCA_EXPERIMENTAL doca_error_t doca_argp_destroy(void)
ARG Parser destroy.
#define DOCA_DEVINFO_IPV4_ADDR_SIZE
Length of IPv4 address.
Definition: doca_dev.h:293
DOCA_STABLE doca_error_t doca_devinfo_get_ipv6_addr(const struct doca_devinfo *devinfo, uint8_t *ipv6_addr, uint32_t size)
Get the IPv6 address of a DOCA devinfo.
DOCA_STABLE doca_error_t doca_devinfo_get_ipv4_addr(const struct doca_devinfo *devinfo, uint8_t *ipv4_addr, uint32_t size)
Get the IPv4 address of a DOCA devinfo.
DOCA_STABLE struct doca_devinfo * doca_dev_as_devinfo(const struct doca_dev *dev)
Get local device info from device. This should be useful when wanting to query information about devi...
#define DOCA_DEVINFO_IPV6_ADDR_SIZE
Length of IPv6 address.
Definition: doca_dev.h:297
DOCA_STABLE doca_error_t doca_dev_close(struct doca_dev *dev)
Destroy allocated local device instance.
DOCA_EXPERIMENTAL doca_error_t doca_dpdk_port_probe(struct doca_dev *dev, const char *devargs)
Attach a DPDK port specified by DOCA device.
enum doca_error doca_error_t
DOCA API return codes.
DOCA_STABLE const char * doca_error_get_descr(doca_error_t error)
Returns the description string of an error code.
@ DOCA_SUCCESS
Definition: doca_error.h:38
@ DOCA_ERROR_DRIVER
Definition: doca_error.h:59
@ DOCA_FLOW_L3_TYPE_IP6
@ DOCA_FLOW_L3_TYPE_IP4
DOCA_EXPERIMENTAL doca_error_t doca_log_backend_create_standard(void)
Create default, non configurable backend for application messages.
#define DOCA_LOG_ERR(format,...)
Generates an ERROR application log message.
Definition: doca_log.h:466
#define DOCA_LOG_WARN(format,...)
Generates a WARNING application log message.
Definition: doca_log.h:476
#define DOCA_LOG_INFO(format,...)
Generates an INFO application log message.
Definition: doca_log.h:486
DOCA_EXPERIMENTAL doca_error_t doca_log_backend_create_with_file_sdk(FILE *fptr, struct doca_log_backend **backend)
Create a logging backend with a FILE* stream for SDK messages.
DOCA_EXPERIMENTAL doca_error_t doca_log_backend_set_sdk_level(struct doca_log_backend *backend, uint32_t level)
Set the log level limit for SDK logging backends.
@ DOCA_LOG_LEVEL_WARNING
Definition: doca_log.h:47
std::string to_string(storage::control::message_type type)
DOCA_LOG_REGISTER(PSP_GATEWAY)
int main(int argc, char **argv)
Definition: psp_gateway.cpp:81
volatile bool force_quit
Definition: psp_gateway.cpp:59
static void signal_handler(int signum)
Signal handler function (SIGINT and SIGTERM signals)
Definition: psp_gateway.cpp:66
static constexpr uint32_t DEFAULT_CRYPT_OFFSET_IPV6
Definition: psp_gw_config.h:56
static const uint32_t DEFAULT_PSP_VERSION
Definition: psp_gw_config.h:46
static constexpr uint32_t DEFAULT_CRYPT_OFFSET_VC_ENABLED_IPV6
Definition: psp_gw_config.h:57
static constexpr uint32_t DEFAULT_CRYPT_OFFSET_VC_ENABLED_IPV4
Definition: psp_gw_config.h:54
@ PSP_GW_MODE_TUNNEL
Definition: psp_gw_config.h:87
static constexpr uint32_t DEFAULT_CRYPT_OFFSET_IPV4
Definition: psp_gw_config.h:53
doca_error_t psp_gw_parse_config_file(psp_gw_app_config *app_config)
Parses the configuration JSON file that was passed to the application.
doca_error_t psp_gw_argp_exec(int &argc, char *argv[], psp_gw_app_config *app_config)
Parses command-line arguments to the application.
int lcore_pkt_proc_func(void *lcore_args)
The entry point for each L-Core's main processing loop. Each L-Core polls a different Rx queue on the...
std::string mac_to_string(const rte_ether_addr &mac_addr)
Converts a MAC/ethernet address to a C++ string.
std::string ip_to_string(const struct doca_flow_ip_addr &ip_addr)
Converts a DOCA Flow IP address struct to a C++ string.
doca_be32_t ipv4_addr
doca_be32_t ipv6_addr[4]
enum doca_flow_l3_type type
The parameters needed by each L-Core's main loop.
PSP_GatewayImpl * psp_svc
psp_pf_dev * pf_dev
PSP_GatewayFlows * psp_flows
describes the configuration of the PSP networking service on the local host.
Maintains the state of the host PF.
Definition: psp_gw_flows.h:47
struct doca_flow_ip_addr src_pip
Definition: psp_gw_flows.h:55
rte_ether_addr src_mac
Definition: psp_gw_flows.h:52
doca_dev * dev
Definition: psp_gw_flows.h:48
std::string src_mac_str
Definition: psp_gw_flows.h:53
std::string src_pip_str
Definition: psp_gw_flows.h:56
uint16_t port_id
Definition: psp_gw_flows.h:49
static int nb_ports
Definition: switch_core.c:44