26 #include <arpa/inet.h>
28 #include <grpcpp/client_context.h>
29 #include <grpcpp/create_channel.h>
46 DEBUG_KEYS(config->debug_keys)
59 const auto *eth_hdr = rte_pktmbuf_mtod(packet,
struct rte_ether_hdr *);
66 rte_pktmbuf_mtod_offset(packet,
struct rte_ipv4_hdr *,
sizeof(
struct rte_ether_hdr));
74 const auto *ipv6_hdr =
75 rte_pktmbuf_mtod_offset(packet,
struct rte_ipv6_hdr *,
sizeof(
struct rte_ether_hdr));
85 if (
sessions.count({src_vip, dst_vip}) == 0) {
87 struct ip_pair vip_pair = {src_vip_addr, dst_vip_addr};
103 DOCA_LOG_ERR(
"Failed to resubmit packet from vnet addr %s to %s on port %d",
114 bool supply_reverse_params,
115 bool suppress_failure_msg,
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);
125 const std::string &peer_svc_pip = peer->
svc_addr;
127 int vip_pair_id = -1;
129 auto *stub = get_stub(peer_svc_pip);
131 ::grpc::ClientContext context;
132 ::psp_gateway::MultiTunnelRequest request;
133 request.set_request_id(++next_request_id);
136 if (supply_reverse_params) {
137 result = generate_keys_spis(key_len_bits, nb_pairs, keys.data(), spis.data());
139 DOCA_LOG_ERR(
"Failed to generate SPI/Key's for peer %s: %s",
140 peer_svc_pip.c_str(),
146 for (
size_t vip_pair_idx = 0; vip_pair_idx < peer->
vip_pairs.size(); vip_pair_idx++) {
155 vip_pair_id = vip_pair_idx;
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();
162 single_request->set_inner_type(4);
164 single_request->set_inner_type(6);
167 single_request->set_virt_src_ip(local_vip);
168 single_request->set_virt_dst_ip(peer_vip);
172 if (supply_reverse_params) {
174 &(keys[spi_key_idx * key_len_words]),
176 single_request->mutable_reverse_params());
177 debug_key(
"Generated",
178 single_request->reverse_params().encryption_key().c_str(),
182 auto &session =
sessions[{local_vip, peer_vip}];
183 session.spi_ingress = single_request->reverse_params().spi();
186 session.pkt_count_ingress = UINT64_MAX;
190 DOCA_LOG_ERR(
"Failed to open ACL (%s <- %s) on SPI %d: %s",
201 session.spi_ingress);
206 if (has_vip_pair && vip_pair_id == -1) {
211 ::psp_gateway::MultiTunnelResponse response;
212 ::grpc::Status status = stub->RequestMultipleTunnelParams(&context, request, &response);
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());
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");
235 result = prepare_session(peer_svc_pip,
237 response.tunnels_params(i),
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(),
251 DOCA_LOG_ERR(
"Failed to add encrypt entries for peer %s: %s",
252 peer_svc_pip.c_str(),
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)
263 DOCA_LOG_DBG(
"Adding %d encrypt entries for peer %s", (
int)new_sessions_keys.size(), peer_svc_addr.c_str());
265 uint64_t start_time = rte_get_tsc_cycles();
266 for (
int i = 0; i < (int)new_sessions_keys.size(); i++) {
271 peer_svc_addr.c_str(),
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(),
288 doca_error_t PSP_GatewayImpl::prepare_session(std::string peer_svc_addr,
290 const psp_gateway::TunnelParameters ¶ms,
291 std::vector<psp_session_and_key_t> &sessions_keys_prepared)
293 std::string peer_vip, local_vip;
297 if (!is_psp_ver_supported(params.psp_version())) {
298 DOCA_LOG_ERR(
"Request for unsupported PSP version %d", params.psp_version());
302 uint32_t key_len_bytes = psp_version_to_key_length_bits(params.psp_version()) / 8;
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);
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");
318 auto &session =
sessions[session_pair];
319 session.dst_vip = vip_pair.
dst_vip;
320 session.src_vip = vip_pair.
src_vip;
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();
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());
335 DOCA_LOG_ERR(
"Failed to parse dst_pip %s", params.ip_addr().c_str());
339 sessions_keys_prepared.push_back({&session, enc_key});
344 int PSP_GatewayImpl::select_psp_version(const ::psp_gateway::MultiTunnelRequest *request)
const
346 for (
int ver : request->psp_versions_accepted()) {
347 if (is_psp_ver_supported(ver) > 0)
354 const ::psp_gateway::MultiTunnelRequest *request,
355 ::psp_gateway::MultiTunnelResponse *response)
358 std::string peer = context ? context->peer()
362 int psp_ver = select_psp_version(request);
364 std::string supported_psp_versions =
"[ ";
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;
372 return ::grpc::Status(::grpc::INVALID_ARGUMENT, error_str);
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());
379 result = generate_keys_spis(key_len_bits, request->tunnels_size(), keys.data(), spis.data());
381 DOCA_LOG_ERR(
"Failed to generate SPI/Key's for peer %s: %s",
384 return ::grpc::Status(::grpc::RESOURCE_EXHAUSTED,
"Failed to generate SPI/Key");
387 response->set_request_id(request->request_id());
389 std::vector<psp_session_and_key_t> reversed_sessions_keys;
391 for (
int tun_idx = 0; tun_idx < request->tunnels_size(); tun_idx++) {
392 const ::psp_gateway::SingleTunnelRequest &single_request = request->tunnels(tun_idx);
394 ::psp_gateway::TunnelParameters *params = response->add_tunnels_params();
395 std::string peer_vip_str = single_request.virt_src_ip();
396 std::string local_vip_str = single_request.virt_dst_ip();
403 return ::grpc::Status(grpc::INVALID_ARGUMENT,
"Failed to parse virt_dst_ip: " + local_vip_str);
406 return ::grpc::Status(grpc::INVALID_ARGUMENT,
"Failed to parse virt_src_ip: " + peer_vip_str);
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",
413 peer_vip_str.c_str(),
415 debug_key(
"Generated", params->encryption_key().c_str(), key_len_bits / 8);
418 auto &session =
sessions[{local_vip_str, peer_vip_str}];
419 session.spi_ingress = params->spi();
422 session.pkt_count_ingress = UINT64_MAX;
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(),
431 return ::grpc::Status(grpc::INTERNAL,
"Failed to create ingress ACL session flow");
435 local_vip_str.c_str(),
436 peer_vip_str.c_str(),
437 session.spi_ingress);
440 if (single_request.has_reverse_params()) {
441 if ((single_request.reverse_params().encap_type() == 4 &&
443 (single_request.reverse_params().encap_type() == 6 &&
446 return ::grpc::Status(::grpc::INVALID_ARGUMENT,
"Received invalid encap type");
448 struct ip_pair vip_pair = {local_vip, peer_vip};
449 result = prepare_session(peer,
451 single_request.reverse_params(),
452 reversed_sessions_keys);
454 return ::grpc::Status(::grpc::UNKNOWN,
455 "Failed to prepare session for peer " +
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(),
466 if (reversed_sessions_keys.size() > 0) {
469 DOCA_LOG_ERR(
"Failed to add encrypt entries for peer %s: %s",
472 return ::grpc::Status(grpc::INTERNAL,
"Failed to create encrypt entries");
476 return ::grpc::Status::OK;
479 void PSP_GatewayImpl::fill_tunnel_params(
int psp_ver, uint32_t *key, uint32_t spi, psp_gateway::TunnelParameters *params)
481 uint32_t key_len_bits = psp_version_to_key_length_bits(psp_ver);
482 uint32_t key_len_bytes = key_len_bits / 8;
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);
491 params->set_encap_type(4);
493 params->set_encap_type(6);
496 doca_error_t PSP_GatewayImpl::generate_keys_spis(uint32_t key_len_bits,
497 uint32_t nr_keys_spis,
502 struct doca_flow_crypto_psp_spi_key_bulk *bulk_key_gen =
nullptr;
505 auto key_array_size = key_len_bits / 32;
507 DOCA_LOG_DBG(
"Generating %d SPI/Key pairs", nr_keys_spis);
515 uint64_t start_time = rte_get_tsc_cycles();
523 for (uint32_t i = 0; i < nr_keys_spis; i++) {
524 uint32_t *cur_key = keys + (i * key_array_size);
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",
549 const ::psp_gateway::KeyRotationRequest *request,
550 ::psp_gateway::KeyRotationResponse *response)
553 DOCA_LOG_DBG(
"Received PSP Master Key Rotation Request");
555 response->set_request_id(request->request_id());
557 if (request->issue_new_keys()) {
558 return ::grpc::Status(::grpc::StatusCode::UNIMPLEMENTED,
"Re-key not implemented");
563 return ::grpc::Status(::grpc::StatusCode::UNKNOWN,
"Key Rotation Failed");
566 return ::grpc::Status::OK;
571 size_t num_connected = 0;
572 for (
auto peer_iter = peers.begin(); peer_iter != peers.end(); ) {
573 doca_error_t result = request_tunnel_to_host(&*peer_iter,
nullptr,
false,
true,
false);
576 peer_iter = peers.erase(peer_iter);
581 return num_connected;
597 uint32_t PSP_GatewayImpl::next_crypto_id(
void)
602 return next_crypto_id_++;
605 ::psp_gateway::PSP_Gateway::Stub *PSP_GatewayImpl::get_stub(
const std::string &peer_ip)
607 auto stubs_iter = stubs.find(peer_ip);
608 if (stubs_iter != stubs.end()) {
609 return stubs_iter->second.get();
612 std::string peer_addr = peer_ip;
613 if (peer_addr.find(
":") == std::string::npos) {
616 grpc::ChannelArguments args;
617 args.SetMaxReceiveMessageSize(10 * 1024 * 1024);
618 auto channel = grpc::CreateCustomChannel(peer_addr, grpc::InsecureChannelCredentials(), args);
619 stubs_iter = stubs.emplace(peer_ip, psp_gateway::PSP_Gateway::NewStub(channel)).first;
621 DOCA_LOG_INFO(
"Created gRPC stub for peer %s", peer_addr.c_str());
623 return stubs_iter->second.get();
626 void PSP_GatewayImpl::debug_key(
const char *msg_prefix,
const void *key,
size_t key_size_bytes)
const
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]);
637 j += sprintf(key_str + j,
" ");
The entity which owns all the doca flow shared resources and flow pipes (but not sessions).
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...
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
@ DOCA_ERROR_UNSUPPORTED_VERSION
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
#define DOCA_LOG_ERR(format,...)
Generates an ERROR application log message.
#define DOCA_LOG_WARN(format,...)
Generates a WARNING application log message.
#define DOCA_LOG_INFO(format,...)
Generates an INFO application log message.
#define DOCA_LOG_DBG(format,...)
Generates a DEBUG application log message.
std::string to_string(storage::control::message_type type)
static constexpr uint16_t PSP_PERF_KEY_GEN_PRINT
static constexpr uint16_t PSP_PERF_INSERTION_PRINT
const std::set< uint32_t > SUPPORTED_PSP_VERSIONS
std::pair< std::string, std::string > session_key
static constexpr uint32_t IPV6_ADDR_LEN
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.
enum doca_flow_l3_type type
doca_flow_ip_addr dst_vip
doca_flow_ip_addr src_vip
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.
std::vector< ip_pair > vip_pairs
doca_flow_port * port_obj