NVIDIA DOCA SDK Data Center on a Chip Framework Documentation
psp_gw_svc_impl.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 #include <arpa/inet.h>
27 
28 #include <grpcpp/client_context.h>
29 #include <grpcpp/create_channel.h>
30 
31 #include <doca_flow_crypto.h>
32 #include <doca_log.h>
33 
34 #include <psp_gw_svc_impl.h>
35 #include <psp_gw_config.h>
36 #include <psp_gw_flows.h>
37 #include <psp_gw_pkt_rss.h>
38 #include <psp_gw_utils.h>
39 
40 DOCA_LOG_REGISTER(PSP_GW_SVC);
41 
43  : config(config),
44  psp_flows(psp_flows),
45  pf(psp_flows->pf()),
46  DEBUG_KEYS(config->debug_keys)
47 {
48 }
49 
51 {
52  std::string dst_vip;
53  std::string src_vip;
54  struct doca_flow_ip_addr dst_vip_addr;
55  struct doca_flow_ip_addr src_vip_addr;
56  if (config->create_tunnels_at_startup)
57  return DOCA_SUCCESS; // no action; tunnels to be created by the main loop
58 
59  const auto *eth_hdr = rte_pktmbuf_mtod(packet, struct rte_ether_hdr *);
60  if (config->inner == DOCA_FLOW_L3_TYPE_IP4 && eth_hdr->ether_type != RTE_BE16(RTE_ETHER_TYPE_IPV4))
61  return DOCA_SUCCESS; // no action
62  if (config->inner == DOCA_FLOW_L3_TYPE_IP6 && eth_hdr->ether_type != RTE_BE16(RTE_ETHER_TYPE_IPV6))
63  return DOCA_SUCCESS; // no action
64  if (config->inner == DOCA_FLOW_L3_TYPE_IP4) {
65  const auto *ipv4_hdr =
66  rte_pktmbuf_mtod_offset(packet, struct rte_ipv4_hdr *, sizeof(struct rte_ether_hdr));
67  dst_vip = ipv4_to_string(ipv4_hdr->dst_addr);
68  src_vip = ipv4_to_string(ipv4_hdr->src_addr);
69  dst_vip_addr.type = DOCA_FLOW_L3_TYPE_IP4;
70  src_vip_addr.type = DOCA_FLOW_L3_TYPE_IP4;
71  dst_vip_addr.ipv4_addr = ipv4_hdr->dst_addr;
72  src_vip_addr.ipv4_addr = ipv4_hdr->src_addr;
73  } else {
74  const auto *ipv6_hdr =
75  rte_pktmbuf_mtod_offset(packet, struct rte_ipv6_hdr *, sizeof(struct rte_ether_hdr));
76  dst_vip = ipv6_to_string((uint32_t *)ipv6_hdr->dst_addr);
77  src_vip = ipv6_to_string((uint32_t *)ipv6_hdr->src_addr);
78  dst_vip_addr.type = DOCA_FLOW_L3_TYPE_IP6;
79  src_vip_addr.type = DOCA_FLOW_L3_TYPE_IP6;
80  memcpy(dst_vip_addr.ipv6_addr, ipv6_hdr->dst_addr, IPV6_ADDR_LEN);
81  memcpy(src_vip_addr.ipv6_addr, ipv6_hdr->src_addr, IPV6_ADDR_LEN);
82  }
83 
84  // Create the new tunnel instance, if one does not already exist
85  if (sessions.count({src_vip, dst_vip}) == 0) {
86  // Determine the peer which owns the virtual destination
87  struct ip_pair vip_pair = {src_vip_addr, dst_vip_addr};
88  auto *peer = lookup_vip_pair(vip_pair);
89  if (!peer) {
90  DOCA_LOG_WARN("Virtual Destination IP Addr not found: %s", dst_vip.c_str());
91  return DOCA_ERROR_NOT_FOUND;
92  }
93 
94  doca_error_t result = request_tunnel_to_host(peer, &vip_pair, true, false, true);
95  if (result != DOCA_SUCCESS) {
96  return result;
97  }
98  }
99 
100  // A new tunnel was created; we can now resubmit the packet
101  // and it will be encrypted and sent to the right port.
102  if (!reinject_packet(packet, pf->port_id)) {
103  DOCA_LOG_ERR("Failed to resubmit packet from vnet addr %s to %s on port %d",
104  src_vip.c_str(),
105  dst_vip.c_str(),
106  pf->port_id);
107  return DOCA_ERROR_FULL;
108  }
109  return DOCA_SUCCESS;
110 }
111 
112 doca_error_t PSP_GatewayImpl::request_tunnel_to_host(struct psp_gw_peer *peer,
113  struct ip_pair *vip_pair,
114  bool supply_reverse_params,
115  bool suppress_failure_msg,
116  bool has_vip_pair)
117 {
119  uint32_t key_len_bits = psp_version_to_key_length_bits(config->net_config.default_psp_proto_ver);
120  uint32_t key_len_words = key_len_bits / 32;
121  uint32_t nb_pairs = has_vip_pair ? 1 : peer->vip_pairs.size();
122  std::vector<uint32_t> keys(nb_pairs * key_len_words);
123  std::vector<uint32_t> spis(nb_pairs);
124 
125  const std::string &peer_svc_pip = peer->svc_addr;
126  int spi_key_idx = 0;
127  int vip_pair_id = -1;
128 
129  auto *stub = get_stub(peer_svc_pip);
130 
131  ::grpc::ClientContext context;
132  ::psp_gateway::MultiTunnelRequest request;
133  request.set_request_id(++next_request_id);
134  request.add_psp_versions_accepted(config->net_config.default_psp_proto_ver);
135 
136  if (supply_reverse_params) {
137  result = generate_keys_spis(key_len_bits, nb_pairs, keys.data(), spis.data());
138  if (result != DOCA_SUCCESS) {
139  DOCA_LOG_ERR("Failed to generate SPI/Key's for peer %s: %s",
140  peer_svc_pip.c_str(),
142  return result;
143  }
144  }
145 
146  for (size_t vip_pair_idx = 0; vip_pair_idx < peer->vip_pairs.size(); vip_pair_idx++) {
147  doca_flow_ip_addr *peer_virt_ip = &peer->vip_pairs[vip_pair_idx].dst_vip;
148  doca_flow_ip_addr *local_virt_ip = &peer->vip_pairs[vip_pair_idx].src_vip;
149 
150  if (has_vip_pair) {
151  if (!is_ip_equal(peer_virt_ip, &vip_pair->dst_vip) ||
152  !is_ip_equal(local_virt_ip, &vip_pair->src_vip))
153  continue;
154  else
155  vip_pair_id = vip_pair_idx;
156  } else
157  spi_key_idx = vip_pair_idx;
158  std::string local_vip;
159  std::string peer_vip;
160  ::psp_gateway::SingleTunnelRequest *single_request = request.add_tunnels();
161  if (config->inner == DOCA_FLOW_L3_TYPE_IP4)
162  single_request->set_inner_type(4);
163  else
164  single_request->set_inner_type(6);
165  local_vip = ip_to_string(*local_virt_ip);
166  peer_vip = ip_to_string(*peer_virt_ip);
167  single_request->set_virt_src_ip(local_vip);
168  single_request->set_virt_dst_ip(peer_vip);
169 
170  // Save a round-trip, if a local virtual IP was given.
171  // Otherwise, expect the peer to send a separate request.
172  if (supply_reverse_params) {
173  fill_tunnel_params(config->net_config.default_psp_proto_ver,
174  &(keys[spi_key_idx * key_len_words]),
175  spis[spi_key_idx],
176  single_request->mutable_reverse_params());
177  debug_key("Generated",
178  single_request->reverse_params().encryption_key().c_str(),
179  key_len_bits / 8);
180 
181  if (!config->disable_ingress_acl) {
182  auto &session = sessions[{local_vip, peer_vip}];
183  session.spi_ingress = single_request->reverse_params().spi();
184  copy_ip_addr(*local_virt_ip, session.src_vip);
185  copy_ip_addr(*peer_virt_ip, session.dst_vip);
186  session.pkt_count_ingress = UINT64_MAX;
187 
188  result = psp_flows->add_ingress_acl_entry(&session);
189  if (result != DOCA_SUCCESS) {
190  DOCA_LOG_ERR("Failed to open ACL (%s <- %s) on SPI %d: %s",
191  local_vip.c_str(),
192  peer_vip.c_str(),
193  session.spi_ingress,
195  return result;
196  }
197 
198  DOCA_LOG_DBG("Opened ACL (%s <- %s) on SPI %d",
199  local_vip.c_str(),
200  peer_vip.c_str(),
201  session.spi_ingress);
202  }
203  }
204  }
205 
206  if (has_vip_pair && vip_pair_id == -1) {
207  DOCA_LOG_ERR("Virtual IPs not found");
208  return DOCA_ERROR_NOT_FOUND;
209  }
210 
211  ::psp_gateway::MultiTunnelResponse response;
212  ::grpc::Status status = stub->RequestMultipleTunnelParams(&context, request, &response);
213 
214  if (!status.ok() || response.tunnels_params_size() != request.tunnels_size()) {
215  if (!suppress_failure_msg) {
216  DOCA_LOG_ERR("Request for new SPI/Key's to peer %s failed: %s",
217  peer_svc_pip.c_str(),
218  status.error_message().c_str());
219  }
220  return DOCA_ERROR_IO_FAILED;
221  }
222 
223  std::vector<psp_session_and_key_t> new_session_keys;
224  for (int i = 0; i < response.tunnels_params_size(); i++) {
225  if (supply_reverse_params) {
226  if (response.tunnels_params(i).encap_type() !=
227  request.tunnels(i).reverse_params().encap_type()) {
228  if (!suppress_failure_msg)
229  DOCA_LOG_ERR("Encap type is different between request and response");
231  }
232  }
233  if (!has_vip_pair)
234  vip_pair_id = i;
235  result = prepare_session(peer_svc_pip,
236  peer->vip_pairs[vip_pair_id],
237  response.tunnels_params(i),
238  new_session_keys);
239  if (result != DOCA_SUCCESS) {
240  DOCA_LOG_ERR("Failed to prepare session for peer %s, request %ld: (%s -> %s): %s",
241  peer_svc_pip.c_str(),
242  request.request_id(),
243  ip_to_string(peer->vip_pairs[i].src_vip).c_str(),
244  ip_to_string(peer->vip_pairs[i].dst_vip).c_str(),
246  return result;
247  }
248  }
249  result = add_encrypt_entries(new_session_keys, peer_svc_pip);
250  if (result != DOCA_SUCCESS) {
251  DOCA_LOG_ERR("Failed to add encrypt entries for peer %s: %s",
252  peer_svc_pip.c_str(),
254  return result;
255  }
256 
257  return DOCA_SUCCESS;
258 }
259 
260 doca_error_t PSP_GatewayImpl::add_encrypt_entries(std::vector<psp_session_and_key_t> &new_sessions_keys,
261  std::string peer_svc_addr)
262 {
263  DOCA_LOG_DBG("Adding %d encrypt entries for peer %s", (int)new_sessions_keys.size(), peer_svc_addr.c_str());
264 
265  uint64_t start_time = rte_get_tsc_cycles();
266  for (int i = 0; i < (int)new_sessions_keys.size(); i++) {
268  psp_flows->add_encrypt_entry(new_sessions_keys[i].first, new_sessions_keys[i].second);
269  if (result != DOCA_SUCCESS) {
270  DOCA_LOG_ERR("Failed to add encrypt entry for %s: %s",
271  peer_svc_addr.c_str(),
273  return result;
274  }
275  }
276  uint64_t end_time = rte_get_tsc_cycles();
277  double total_time = (end_time - start_time) / (double)rte_get_tsc_hz();
278  double kilo_eps = 1e-3 * new_sessions_keys.size() / total_time;
280  DOCA_LOG_INFO("Added %d encrypt entries in %f seconds, %f Kilo-EPS",
281  (int)new_sessions_keys.size(),
282  total_time,
283  kilo_eps);
284  }
285  return DOCA_SUCCESS;
286 }
287 
288 doca_error_t PSP_GatewayImpl::prepare_session(std::string peer_svc_addr,
289  struct ip_pair &vip_pair,
290  const psp_gateway::TunnelParameters &params,
291  std::vector<psp_session_and_key_t> &sessions_keys_prepared)
292 {
293  std::string peer_vip, local_vip;
294  peer_vip = ip_to_string(vip_pair.dst_vip);
295  local_vip = ip_to_string(vip_pair.src_vip);
296 
297  if (!is_psp_ver_supported(params.psp_version())) {
298  DOCA_LOG_ERR("Request for unsupported PSP version %d", params.psp_version());
300  }
301 
302  uint32_t key_len_bytes = psp_version_to_key_length_bits(params.psp_version()) / 8;
303 
304  if (params.encryption_key().size() != key_len_bytes) {
305  DOCA_LOG_ERR("Request for new SPI/Key to peer %s failed: %s (%ld)",
306  peer_svc_addr.c_str(),
307  "Invalid encryption key length",
308  params.encryption_key().size() * 8);
309  return DOCA_ERROR_IO_FAILED;
310  }
311 
312  uint32_t crypto_id = next_crypto_id();
313  if (crypto_id == UINT32_MAX) {
314  DOCA_LOG_ERR("Exhausted available crypto_ids; cannot complete new tunnel");
315  return DOCA_ERROR_NO_MEMORY;
316  }
317  session_key session_pair = {local_vip, peer_vip};
318  auto &session = sessions[session_pair];
319  session.dst_vip = vip_pair.dst_vip; // allready set if other direction was supplied
320  session.src_vip = vip_pair.src_vip; // allready set if other direction was supplied
321  session.spi_egress = params.spi();
322  session.crypto_id = crypto_id;
323  session.psp_proto_ver = params.psp_version();
324  session.vc = params.virt_cookie();
325  void *enc_key = (void *)params.encryption_key().c_str();
326 
327  if (rte_ether_unformat_addr(params.mac_addr().c_str(), &session.dst_mac)) {
328  DOCA_LOG_ERR("Failed to convert mac addr: %s", params.mac_addr().c_str());
329  sessions.erase(session_pair);
331  }
332 
333  doca_flow_l3_type enforce_l3_type = params.encap_type() == 4 ? DOCA_FLOW_L3_TYPE_IP4 : DOCA_FLOW_L3_TYPE_IP6;
334  if (parse_ip_addr(params.ip_addr(), enforce_l3_type, &session.dst_pip) != DOCA_SUCCESS) {
335  DOCA_LOG_ERR("Failed to parse dst_pip %s", params.ip_addr().c_str());
336  sessions.erase(session_pair);
338  }
339  sessions_keys_prepared.push_back({&session, enc_key});
340 
341  return DOCA_SUCCESS;
342 }
343 
344 int PSP_GatewayImpl::select_psp_version(const ::psp_gateway::MultiTunnelRequest *request) const
345 {
346  for (int ver : request->psp_versions_accepted()) {
347  if (is_psp_ver_supported(ver) > 0)
348  return ver;
349  }
350  return -1;
351 }
352 
353 ::grpc::Status PSP_GatewayImpl::RequestMultipleTunnelParams(::grpc::ServerContext *context,
354  const ::psp_gateway::MultiTunnelRequest *request,
355  ::psp_gateway::MultiTunnelResponse *response)
356 {
358  std::string peer = context ? context->peer() // note: NOT authenticated
359  :
360  "[TESTING]";
361 
362  int psp_ver = select_psp_version(request);
363  if (psp_ver < 0) {
364  std::string supported_psp_versions = "[ ";
365  for (auto psp_ver : SUPPORTED_PSP_VERSIONS) {
366  supported_psp_versions += std::to_string(psp_ver) + " ";
367  }
368  supported_psp_versions += "]";
369  std::string error_str = "Rejecting tunnel request from peer " + peer + ", PSP version must be one of " +
370  supported_psp_versions;
371  DOCA_LOG_ERR("%s", error_str.c_str());
372  return ::grpc::Status(::grpc::INVALID_ARGUMENT, error_str);
373  }
374  uint32_t key_len_bits = psp_version_to_key_length_bits(psp_ver);
375  uint32_t key_len_words = key_len_bits / 32;
376  std::vector<uint32_t> keys(request->tunnels_size() * key_len_words);
377  std::vector<uint32_t> spis(request->tunnels_size());
378 
379  result = generate_keys_spis(key_len_bits, request->tunnels_size(), keys.data(), spis.data());
380  if (result != DOCA_SUCCESS) {
381  DOCA_LOG_ERR("Failed to generate SPI/Key's for peer %s: %s",
382  peer.c_str(),
384  return ::grpc::Status(::grpc::RESOURCE_EXHAUSTED, "Failed to generate SPI/Key");
385  }
386 
387  response->set_request_id(request->request_id());
388 
389  std::vector<psp_session_and_key_t> reversed_sessions_keys;
390 
391  for (int tun_idx = 0; tun_idx < request->tunnels_size(); tun_idx++) {
392  const ::psp_gateway::SingleTunnelRequest &single_request = request->tunnels(tun_idx);
393 
394  ::psp_gateway::TunnelParameters *params = response->add_tunnels_params();
395  std::string peer_vip_str = single_request.virt_src_ip(); // reversed
396  std::string local_vip_str = single_request.virt_dst_ip(); // reversed
397  struct doca_flow_ip_addr peer_vip = {};
398  struct doca_flow_ip_addr local_vip = {};
399 
400  doca_flow_l3_type enforce_l3_type = single_request.inner_type() == 4 ? DOCA_FLOW_L3_TYPE_IP4 :
402  if (parse_ip_addr(local_vip_str, enforce_l3_type, &local_vip) != DOCA_SUCCESS) {
403  return ::grpc::Status(grpc::INVALID_ARGUMENT, "Failed to parse virt_dst_ip: " + local_vip_str);
404  }
405  if (parse_ip_addr(peer_vip_str, enforce_l3_type, &peer_vip) != DOCA_SUCCESS) {
406  return ::grpc::Status(grpc::INVALID_ARGUMENT, "Failed to parse virt_src_ip: " + peer_vip_str);
407  }
408 
409  fill_tunnel_params(psp_ver, &(keys[tun_idx * key_len_words]), spis[tun_idx], params);
410  DOCA_LOG_DBG("#%d: SPI %d generated for virtual addr %s on peer %s",
411  tun_idx,
412  params->spi(),
413  peer_vip_str.c_str(),
414  peer.c_str());
415  debug_key("Generated", params->encryption_key().c_str(), key_len_bits / 8);
416 
417  if (!config->disable_ingress_acl) {
418  auto &session = sessions[{local_vip_str, peer_vip_str}];
419  session.spi_ingress = params->spi();
420  copy_ip_addr(local_vip, session.src_vip);
421  copy_ip_addr(peer_vip, session.dst_vip);
422  session.pkt_count_ingress = UINT64_MAX;
423 
424  result = psp_flows->add_ingress_acl_entry(&session);
425  if (result != DOCA_SUCCESS) {
426  DOCA_LOG_ERR("Failed to open ACL (%s <- %s) on SPI %d: %s",
427  local_vip_str.c_str(),
428  peer_vip_str.c_str(),
429  session.spi_ingress,
431  return ::grpc::Status(grpc::INTERNAL, "Failed to create ingress ACL session flow");
432  }
433 
434  DOCA_LOG_DBG("Opened ACL (%s <- %s) on SPI %d",
435  local_vip_str.c_str(),
436  peer_vip_str.c_str(),
437  session.spi_ingress);
438  }
439 
440  if (single_request.has_reverse_params()) {
441  if ((single_request.reverse_params().encap_type() == 4 &&
442  config->outer == DOCA_FLOW_L3_TYPE_IP6) ||
443  (single_request.reverse_params().encap_type() == 6 &&
444  config->outer == DOCA_FLOW_L3_TYPE_IP4)) {
445  DOCA_LOG_ERR("Invalid encap type");
446  return ::grpc::Status(::grpc::INVALID_ARGUMENT, "Received invalid encap type");
447  }
448  struct ip_pair vip_pair = {local_vip, peer_vip};
449  result = prepare_session(peer,
450  vip_pair,
451  single_request.reverse_params(),
452  reversed_sessions_keys);
453  if (result != DOCA_SUCCESS) {
454  return ::grpc::Status(::grpc::UNKNOWN,
455  "Failed to prepare session for peer " +
456  std::to_string(request->request_id()));
457  }
458  DOCA_LOG_DBG("Created return flow (%s -> %s) on SPI %d to peer %s",
459  local_vip_str.c_str(),
460  peer_vip_str.c_str(),
461  single_request.reverse_params().spi(),
462  peer.c_str());
463  }
464  }
465 
466  if (reversed_sessions_keys.size() > 0) {
467  result = add_encrypt_entries(reversed_sessions_keys, peer);
468  if (result != DOCA_SUCCESS) {
469  DOCA_LOG_ERR("Failed to add encrypt entries for peer %s: %s",
470  peer.c_str(),
472  return ::grpc::Status(grpc::INTERNAL, "Failed to create encrypt entries");
473  }
474  }
475 
476  return ::grpc::Status::OK;
477 }
478 
479 void PSP_GatewayImpl::fill_tunnel_params(int psp_ver, uint32_t *key, uint32_t spi, psp_gateway::TunnelParameters *params)
480 {
481  uint32_t key_len_bits = psp_version_to_key_length_bits(psp_ver);
482  uint32_t key_len_bytes = key_len_bits / 8;
483 
484  params->set_mac_addr(pf->src_mac_str);
485  params->set_ip_addr(pf->src_pip_str);
486  params->set_psp_version(psp_ver);
487  params->set_spi(spi);
488  params->set_encryption_key(key, key_len_bytes);
489  params->set_virt_cookie(0x778899aabbccddee);
490  if (config->outer == DOCA_FLOW_L3_TYPE_IP4)
491  params->set_encap_type(4);
492  else
493  params->set_encap_type(6);
494 }
495 
496 doca_error_t PSP_GatewayImpl::generate_keys_spis(uint32_t key_len_bits,
497  uint32_t nr_keys_spis,
498  uint32_t *keys,
499  uint32_t *spis)
500 {
502  struct doca_flow_crypto_psp_spi_key_bulk *bulk_key_gen = nullptr;
503 
504  auto key_type = key_len_bits == 128 ? DOCA_FLOW_CRYPTO_KEY_128 : DOCA_FLOW_CRYPTO_KEY_256;
505  auto key_array_size = key_len_bits / 32; // 32-bit words
506 
507  DOCA_LOG_DBG("Generating %d SPI/Key pairs", nr_keys_spis);
508 
509  result = doca_flow_crypto_psp_spi_key_bulk_alloc(pf->port_obj, key_type, nr_keys_spis, &bulk_key_gen);
510  if (result != DOCA_SUCCESS || !bulk_key_gen) {
511  DOCA_LOG_ERR("Failed to allocate bulk-key-gen object: %s", doca_error_get_descr(result));
512  return DOCA_ERROR_NO_MEMORY;
513  }
514 
515  uint64_t start_time = rte_get_tsc_cycles();
517  if (result != DOCA_SUCCESS) {
518  DOCA_LOG_ERR("Failed to generate keys and SPIs: %s", doca_error_get_descr(result));
520  return DOCA_ERROR_IO_FAILED;
521  }
522 
523  for (uint32_t i = 0; i < nr_keys_spis; i++) {
524  uint32_t *cur_key = keys + (i * key_array_size);
525  result = doca_flow_crypto_psp_spi_key_bulk_get(bulk_key_gen, i, &spis[i], cur_key);
526  if (result != DOCA_SUCCESS) {
527  DOCA_LOG_ERR("Failed to retrieve SPI/Key: %s", doca_error_get_descr(result));
529  return DOCA_ERROR_IO_FAILED;
530  }
531  }
532 
533  uint64_t end_time = rte_get_tsc_cycles();
534  double total_time = (end_time - start_time) / (double)rte_get_tsc_hz();
535  double kilo_kps = 1e-3 * nr_keys_spis / total_time;
537  DOCA_LOG_INFO("Generated %d SPI/Key pairs in %f seconds, %f KILO-KPS",
538  nr_keys_spis,
539  total_time,
540  kilo_kps);
541  }
542 
544 
545  return DOCA_SUCCESS;
546 }
547 
548 ::grpc::Status PSP_GatewayImpl::RequestKeyRotation(::grpc::ServerContext *context,
549  const ::psp_gateway::KeyRotationRequest *request,
550  ::psp_gateway::KeyRotationResponse *response)
551 {
552  (void)context;
553  DOCA_LOG_DBG("Received PSP Master Key Rotation Request");
554 
555  response->set_request_id(request->request_id());
556 
557  if (request->issue_new_keys()) {
558  return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED, "Re-key not implemented");
559  }
560 
562  if (result != DOCA_SUCCESS) {
563  return ::grpc::Status(::grpc::StatusCode::UNKNOWN, "Key Rotation Failed");
564  }
565 
566  return ::grpc::Status::OK;
567 }
568 
569 size_t PSP_GatewayImpl::try_connect(std::vector<psp_gw_peer> &peers)
570 {
571  size_t num_connected = 0;
572  for (auto peer_iter = peers.begin(); peer_iter != peers.end(); /* increment below */) {
573  doca_error_t result = request_tunnel_to_host(&*peer_iter, nullptr, false, true, false);
574  if (result == DOCA_SUCCESS) {
575  ++num_connected;
576  peer_iter = peers.erase(peer_iter);
577  } else {
578  ++peer_iter;
579  }
580  }
581  return num_connected;
582 }
583 
584 psp_gw_peer *PSP_GatewayImpl::lookup_vip_pair(ip_pair &vip_pair)
585 {
586  return ::lookup_vip_pair(&config->net_config.peers, vip_pair);
587 }
588 
590 {
591  for (auto &session : sessions) {
592  psp_flows->show_session_flow_count(session.first, session.second);
593  }
594  return DOCA_SUCCESS;
595 }
596 
597 uint32_t PSP_GatewayImpl::next_crypto_id(void)
598 {
599  if (next_crypto_id_ > config->max_tunnels) {
600  return UINT32_MAX;
601  }
602  return next_crypto_id_++;
603 }
604 
605 ::psp_gateway::PSP_Gateway::Stub *PSP_GatewayImpl::get_stub(const std::string &peer_ip)
606 {
607  auto stubs_iter = stubs.find(peer_ip);
608  if (stubs_iter != stubs.end()) {
609  return stubs_iter->second.get();
610  }
611 
612  std::string peer_addr = peer_ip;
613  if (peer_addr.find(":") == std::string::npos) {
614  peer_addr += ":" + std::to_string(DEFAULT_HTTP_PORT_NUM);
615  }
616  grpc::ChannelArguments args;
617  args.SetMaxReceiveMessageSize(10 * 1024 * 1024); // 10 MB
618  auto channel = grpc::CreateCustomChannel(peer_addr, grpc::InsecureChannelCredentials(), args);
619  stubs_iter = stubs.emplace(peer_ip, psp_gateway::PSP_Gateway::NewStub(channel)).first;
620 
621  DOCA_LOG_INFO("Created gRPC stub for peer %s", peer_addr.c_str());
622 
623  return stubs_iter->second.get();
624 }
625 
626 void PSP_GatewayImpl::debug_key(const char *msg_prefix, const void *key, size_t key_size_bytes) const
627 {
628  if (!DEBUG_KEYS) {
629  return;
630  }
631 
632  char key_str[key_size_bytes * 3];
633  const uint8_t *key_bytes = (const uint8_t *)key;
634  for (size_t i = 0, j = 0; i < key_size_bytes; i++) {
635  j += sprintf(key_str + j, "%02X", key_bytes[i]);
636  if ((i % 4) == 3) {
637  j += sprintf(key_str + j, " ");
638  }
639  }
640  DOCA_LOG_INFO("%s encryption key: %s", msg_prefix, key_str);
641 }
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_session_flow_count(const session_key session_vips_pair, psp_session_t &session)
Shows flow counters for the given tunnel, if they have changed since the last invocation.
doca_error_t add_ingress_acl_entry(psp_session_t *session)
Adds an ingress ACL entry for the given session to accept the combination of src_vip and SPI.
doca_error_t add_encrypt_entry(psp_session_t *session, const void *encrypt_key)
Adds a flow pipe entry to perform encryption on a new flow to the indicated peer. The caller is respo...
doca_error_t handle_miss_packet(struct rte_mbuf *packet)
Handles any "miss" packets received by RSS which indicate a new tunnel connection is needed.
::grpc::Status RequestKeyRotation(::grpc::ServerContext *context, const ::psp_gateway::KeyRotationRequest *request, ::psp_gateway::KeyRotationResponse *response) override
Requests that the recipient rotate the PSP master key.
PSP_GatewayImpl(psp_gw_app_config *config, PSP_GatewayFlows *psp_flows)
Constructs the object. This operation cannot fail.
::grpc::Status RequestMultipleTunnelParams(::grpc::ServerContext *context, const ::psp_gateway::MultiTunnelRequest *request, ::psp_gateway::MultiTunnelResponse *response) override
Requests that the recipient allocate multiple SPIs and encryption keys so that the initiator can begi...
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 uint16_t sessions
doca_error_t add_encrypt_entries(struct ipsec_security_gw_config *app_cfg, struct ipsec_security_gw_ports_map *ports[], uint16_t queue_id, int nb_rules, int rule_offset)
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_ERROR_INVALID_VALUE
Definition: doca_error.h:44
@ DOCA_ERROR_UNSUPPORTED_VERSION
Definition: doca_error.h:57
@ DOCA_ERROR_NOT_FOUND
Definition: doca_error.h:54
@ DOCA_ERROR_IO_FAILED
Definition: doca_error.h:55
@ DOCA_SUCCESS
Definition: doca_error.h:38
@ DOCA_ERROR_FULL
Definition: doca_error.h:62
@ DOCA_ERROR_NO_MEMORY
Definition: doca_error.h:45
DOCA_EXPERIMENTAL doca_error_t doca_flow_crypto_psp_spi_key_bulk_get(struct doca_flow_crypto_psp_spi_key_bulk *spi_key_bulk, uint32_t spi_key_idx, uint32_t *spi, uint32_t *key)
Get SPI and key for specific index in the bulk.
DOCA_EXPERIMENTAL doca_error_t doca_flow_crypto_psp_spi_key_bulk_free(struct doca_flow_crypto_psp_spi_key_bulk *spi_key_bulk)
Free the memory for spi key bulk.
DOCA_EXPERIMENTAL doca_error_t doca_flow_crypto_psp_master_key_rotate(struct doca_flow_port *port)
Rotate PSP master key.
DOCA_EXPERIMENTAL doca_error_t doca_flow_crypto_psp_spi_key_bulk_generate(struct doca_flow_crypto_psp_spi_key_bulk *spi_key_bulk)
Fill a bulk with new pairs of SPI and key.
DOCA_EXPERIMENTAL doca_error_t doca_flow_crypto_psp_spi_key_bulk_alloc(struct doca_flow_port *port, enum doca_flow_crypto_key_type key_type, uint32_t nr_spi_keys, struct doca_flow_crypto_psp_spi_key_bulk **spi_key_bulk)
Allocate an array of spi and key pairs.
@ DOCA_FLOW_CRYPTO_KEY_128
@ DOCA_FLOW_CRYPTO_KEY_256
doca_flow_l3_type
doca flow layer 3 packet type
@ DOCA_FLOW_L3_TYPE_IP6
@ DOCA_FLOW_L3_TYPE_IP4
#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
#define DOCA_LOG_DBG(format,...)
Generates a DEBUG application log message.
Definition: doca_log.h:496
std::string to_string(storage::control::message_type type)
static constexpr uint16_t PSP_PERF_KEY_GEN_PRINT
Definition: psp_gw_config.h:59
static constexpr uint16_t PSP_PERF_INSERTION_PRINT
Definition: psp_gw_config.h:60
const std::set< uint32_t > SUPPORTED_PSP_VERSIONS
Definition: psp_gw_config.h:45
std::pair< std::string, std::string > session_key
Definition: psp_gw_config.h:79
static constexpr uint32_t IPV6_ADDR_LEN
Definition: psp_gw_config.h:77
bool reinject_packet(struct rte_mbuf *packet, uint16_t port_id)
Used by the psp_svc to re-inject a packet via the Host PF Tx queue after a new tunnel has been establ...
DOCA_LOG_REGISTER(PSP_GW_SVC)
bool is_ip_equal(struct doca_flow_ip_addr *ip_a, struct doca_flow_ip_addr *ip_b)
Compare DOCA Flow IP address struct, return true if addresses are equal.
doca_error_t parse_ip_addr(const std::string &ip_str, doca_flow_l3_type enforce_l3_type, struct doca_flow_ip_addr *ip_addr)
Parse an IP address string into a DOCA Flow IP address struct.
std::string ipv4_to_string(rte_be32_t ipv4_addr)
Converts an IPv4 address to a C++ string.
psp_gw_peer * lookup_vip_pair(std::vector< psp_gw_peer > *peers, ip_pair &vip_pair)
Search for a peer in a vector of peers that holds the same IP pair.
void copy_ip_addr(const struct doca_flow_ip_addr &src, struct doca_flow_ip_addr &dst)
Copy an IP address struct.
std::string ip_to_string(const struct doca_flow_ip_addr &ip_addr)
Converts a DOCA Flow IP address struct to a C++ string.
std::string ipv6_to_string(const uint32_t ipv6_addr[])
Converts an IPv6 address to a C++ string.
doca flow ip address
doca_be32_t ipv4_addr
doca_be32_t ipv6_addr[4]
enum doca_flow_l3_type type
doca_flow_ip_addr dst_vip
Definition: psp_gw_config.h:83
doca_flow_ip_addr src_vip
Definition: psp_gw_config.h:82
uint32_t src_addr
Definition: packets.h:75
uint32_t dst_addr
Definition: packets.h:76
describes the configuration of the PSP networking service on the local host.
enum doca_flow_l3_type inner
bool create_tunnels_at_startup
enum doca_flow_l3_type outer
uint16_t print_perf_flags
struct psp_gw_net_config net_config
std::vector< psp_gw_peer > peers
uint32_t default_psp_proto_ver
Describes a peer which is capable of exchanging traffic flows over a PSP tunnel.
Definition: psp_gw_config.h:98
std::string svc_addr
std::vector< ip_pair > vip_pairs
std::string src_mac_str
Definition: psp_gw_flows.h:53
doca_flow_port * port_obj
Definition: psp_gw_flows.h:50
std::string src_pip_str
Definition: psp_gw_flows.h:56
uint16_t port_id
Definition: psp_gw_flows.h:49