1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (c) 2023 Corigine Systems, Inc. 3 * All rights reserved. 4 */ 5 6 #include "nfp_ipsec.h" 7 8 #include <rte_cryptodev.h> 9 #include <rte_malloc.h> 10 #include <rte_security_driver.h> 11 12 #include <ethdev_driver.h> 13 #include <ethdev_pci.h> 14 15 #include "nfp_common.h" 16 #include "nfp_ctrl.h" 17 #include "nfp_logs.h" 18 #include "nfp_rxtx.h" 19 20 static const struct rte_cryptodev_capabilities nfp_crypto_caps[] = { 21 { 22 .op = RTE_CRYPTO_OP_TYPE_SYMMETRIC, 23 .sym = { 24 .xform_type = RTE_CRYPTO_SYM_XFORM_AUTH, 25 .auth = { 26 .algo = RTE_CRYPTO_AUTH_MD5_HMAC, 27 .block_size = 64, 28 .key_size = { 29 .min = 16, 30 .max = 16, 31 .increment = 0 32 }, 33 .digest_size = { 34 .min = 12, 35 .max = 16, 36 .increment = 4 37 }, 38 }, 39 }, 40 }, 41 { 42 .op = RTE_CRYPTO_OP_TYPE_SYMMETRIC, 43 .sym = { 44 .xform_type = RTE_CRYPTO_SYM_XFORM_AUTH, 45 .auth = { 46 .algo = RTE_CRYPTO_AUTH_SHA1_HMAC, 47 .block_size = 64, 48 .key_size = { 49 .min = 20, 50 .max = 64, 51 .increment = 1 52 }, 53 .digest_size = { 54 .min = 10, 55 .max = 12, 56 .increment = 2 57 }, 58 }, 59 }, 60 }, 61 { 62 .op = RTE_CRYPTO_OP_TYPE_SYMMETRIC, 63 .sym = { 64 .xform_type = RTE_CRYPTO_SYM_XFORM_AUTH, 65 .auth = { 66 .algo = RTE_CRYPTO_AUTH_SHA256_HMAC, 67 .block_size = 64, 68 .key_size = { 69 .min = 32, 70 .max = 32, 71 .increment = 0 72 }, 73 .digest_size = { 74 .min = 12, 75 .max = 16, 76 .increment = 4 77 }, 78 }, 79 }, 80 }, 81 { 82 .op = RTE_CRYPTO_OP_TYPE_SYMMETRIC, 83 .sym = { 84 .xform_type = RTE_CRYPTO_SYM_XFORM_AUTH, 85 .auth = { 86 .algo = RTE_CRYPTO_AUTH_SHA384_HMAC, 87 .block_size = 128, 88 .key_size = { 89 .min = 48, 90 .max = 48, 91 .increment = 0 92 }, 93 .digest_size = { 94 .min = 12, 95 .max = 24, 96 .increment = 12 97 }, 98 }, 99 }, 100 }, 101 { 102 .op = RTE_CRYPTO_OP_TYPE_SYMMETRIC, 103 .sym = { 104 .xform_type = RTE_CRYPTO_SYM_XFORM_AUTH, 105 .auth = { 106 .algo = RTE_CRYPTO_AUTH_SHA512_HMAC, 107 .block_size = 128, 108 .key_size = { 109 .min = 64, 110 .max = 64, 111 .increment = 1 112 }, 113 .digest_size = { 114 .min = 12, 115 .max = 32, 116 .increment = 4 117 }, 118 }, 119 }, 120 }, 121 { 122 .op = RTE_CRYPTO_OP_TYPE_SYMMETRIC, 123 .sym = { 124 .xform_type = RTE_CRYPTO_SYM_XFORM_CIPHER, 125 .cipher = { 126 .algo = RTE_CRYPTO_CIPHER_3DES_CBC, 127 .block_size = 8, 128 .key_size = { 129 .min = 24, 130 .max = 24, 131 .increment = 0 132 }, 133 .iv_size = { 134 .min = 8, 135 .max = 16, 136 .increment = 8 137 }, 138 }, 139 }, 140 }, 141 { 142 .op = RTE_CRYPTO_OP_TYPE_SYMMETRIC, 143 .sym = { 144 .xform_type = RTE_CRYPTO_SYM_XFORM_CIPHER, 145 .cipher = { 146 .algo = RTE_CRYPTO_CIPHER_AES_CBC, 147 .block_size = 16, 148 .key_size = { 149 .min = 16, 150 .max = 32, 151 .increment = 8 152 }, 153 .iv_size = { 154 .min = 8, 155 .max = 16, 156 .increment = 8 157 }, 158 }, 159 }, 160 }, 161 { 162 .op = RTE_CRYPTO_OP_TYPE_SYMMETRIC, 163 .sym = { 164 .xform_type = RTE_CRYPTO_SYM_XFORM_AEAD, 165 .aead = { 166 .algo = RTE_CRYPTO_AEAD_AES_GCM, 167 .block_size = 16, 168 .key_size = { 169 .min = 16, 170 .max = 32, 171 .increment = 8 172 }, 173 .digest_size = { 174 .min = 16, 175 .max = 16, 176 .increment = 0 177 }, 178 .aad_size = { 179 .min = 0, 180 .max = 1024, 181 .increment = 1 182 }, 183 .iv_size = { 184 .min = 8, 185 .max = 16, 186 .increment = 4 187 } 188 }, 189 }, 190 }, 191 { 192 .op = RTE_CRYPTO_OP_TYPE_SYMMETRIC, 193 .sym = { 194 .xform_type = RTE_CRYPTO_SYM_XFORM_AEAD, 195 .aead = { 196 .algo = RTE_CRYPTO_AEAD_CHACHA20_POLY1305, 197 .block_size = 16, 198 .key_size = { 199 .min = 32, 200 .max = 32, 201 .increment = 0 202 }, 203 .digest_size = { 204 .min = 16, 205 .max = 16, 206 .increment = 0 207 }, 208 .aad_size = { 209 .min = 0, 210 .max = 1024, 211 .increment = 1 212 }, 213 .iv_size = { 214 .min = 8, 215 .max = 16, 216 .increment = 4 217 } 218 }, 219 }, 220 }, 221 { 222 .op = RTE_CRYPTO_OP_TYPE_UNDEFINED, 223 .sym = { 224 .xform_type = RTE_CRYPTO_SYM_XFORM_NOT_SPECIFIED 225 }, 226 } 227 }; 228 229 static const struct rte_security_capability nfp_security_caps[] = { 230 { /* IPsec Inline Crypto Tunnel Egress */ 231 .action = RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO, 232 .protocol = RTE_SECURITY_PROTOCOL_IPSEC, 233 .ipsec = { 234 .mode = RTE_SECURITY_IPSEC_SA_MODE_TUNNEL, 235 .direction = RTE_SECURITY_IPSEC_SA_DIR_EGRESS, 236 .proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP, 237 .options = { 238 .udp_encap = 1, 239 .stats = 1, 240 .esn = 1 241 } 242 }, 243 .crypto_capabilities = nfp_crypto_caps 244 }, 245 { /* IPsec Inline Crypto Tunnel Ingress */ 246 .action = RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO, 247 .protocol = RTE_SECURITY_PROTOCOL_IPSEC, 248 .ipsec = { 249 .mode = RTE_SECURITY_IPSEC_SA_MODE_TUNNEL, 250 .direction = RTE_SECURITY_IPSEC_SA_DIR_INGRESS, 251 .proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP, 252 .options = { 253 .udp_encap = 1, 254 .stats = 1, 255 .esn = 1 256 } 257 }, 258 .crypto_capabilities = nfp_crypto_caps, 259 .ol_flags = RTE_SECURITY_TX_OLOAD_NEED_MDATA 260 }, 261 { /* IPsec Inline Crypto Transport Egress */ 262 .action = RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO, 263 .protocol = RTE_SECURITY_PROTOCOL_IPSEC, 264 .ipsec = { 265 .mode = RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT, 266 .direction = RTE_SECURITY_IPSEC_SA_DIR_EGRESS, 267 .proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP, 268 .options = { 269 .udp_encap = 1, 270 .stats = 1, 271 .esn = 1 272 } 273 }, 274 .crypto_capabilities = nfp_crypto_caps 275 }, 276 { /* IPsec Inline Crypto Transport Ingress */ 277 .action = RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO, 278 .protocol = RTE_SECURITY_PROTOCOL_IPSEC, 279 .ipsec = { 280 .mode = RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT, 281 .direction = RTE_SECURITY_IPSEC_SA_DIR_INGRESS, 282 .proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP, 283 .options = { 284 .udp_encap = 1, 285 .stats = 1, 286 .esn = 1 287 } 288 }, 289 .crypto_capabilities = nfp_crypto_caps, 290 .ol_flags = RTE_SECURITY_TX_OLOAD_NEED_MDATA 291 }, 292 { /* IPsec Inline Protocol Tunnel Egress */ 293 .action = RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL, 294 .protocol = RTE_SECURITY_PROTOCOL_IPSEC, 295 .ipsec = { 296 .mode = RTE_SECURITY_IPSEC_SA_MODE_TUNNEL, 297 .direction = RTE_SECURITY_IPSEC_SA_DIR_EGRESS, 298 .proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP, 299 .options = { 300 .udp_encap = 1, 301 .stats = 1, 302 .esn = 1 303 } 304 }, 305 .crypto_capabilities = nfp_crypto_caps 306 }, 307 { /* IPsec Inline Protocol Tunnel Ingress */ 308 .action = RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL, 309 .protocol = RTE_SECURITY_PROTOCOL_IPSEC, 310 .ipsec = { 311 .mode = RTE_SECURITY_IPSEC_SA_MODE_TUNNEL, 312 .direction = RTE_SECURITY_IPSEC_SA_DIR_INGRESS, 313 .proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP, 314 .options = { 315 .udp_encap = 1, 316 .stats = 1, 317 .esn = 1 318 } 319 }, 320 .crypto_capabilities = nfp_crypto_caps, 321 .ol_flags = RTE_SECURITY_TX_OLOAD_NEED_MDATA 322 }, 323 { /* IPsec Inline Protocol Transport Egress */ 324 .action = RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL, 325 .protocol = RTE_SECURITY_PROTOCOL_IPSEC, 326 .ipsec = { 327 .mode = RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT, 328 .direction = RTE_SECURITY_IPSEC_SA_DIR_EGRESS, 329 .proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP, 330 .options = { 331 .udp_encap = 1, 332 .stats = 1, 333 .esn = 1 334 } 335 }, 336 .crypto_capabilities = nfp_crypto_caps 337 }, 338 { /* IPsec Inline Protocol Transport Ingress */ 339 .action = RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL, 340 .protocol = RTE_SECURITY_PROTOCOL_IPSEC, 341 .ipsec = { 342 .mode = RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT, 343 .direction = RTE_SECURITY_IPSEC_SA_DIR_INGRESS, 344 .proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP, 345 .options = { 346 .udp_encap = 1, 347 .stats = 1, 348 .esn = 1 349 } 350 }, 351 .crypto_capabilities = nfp_crypto_caps, 352 .ol_flags = RTE_SECURITY_TX_OLOAD_NEED_MDATA 353 }, 354 { 355 .action = RTE_SECURITY_ACTION_TYPE_NONE 356 } 357 }; 358 359 /* IPsec config message cmd codes */ 360 enum nfp_ipsec_cfg_msg_cmd_codes { 361 NFP_IPSEC_CFG_MSG_ADD_SA, /**< Add a new SA */ 362 NFP_IPSEC_CFG_MSG_INV_SA, /**< Invalidate an existing SA */ 363 NFP_IPSEC_CFG_MSG_MODIFY_SA, /**< Modify an existing SA */ 364 NFP_IPSEC_CFG_MSG_GET_SA_STATS, /**< Report SA counters, flags, etc. */ 365 NFP_IPSEC_CFG_MSG_GET_SEQ_NUMS, /**< Allocate sequence numbers */ 366 NFP_IPSEC_CFG_MSG_LAST 367 }; 368 369 enum nfp_ipsec_cfg_msg_rsp_codes { 370 NFP_IPSEC_CFG_MSG_OK, 371 NFP_IPSEC_CFG_MSG_FAILED, 372 NFP_IPSEC_CFG_MSG_SA_VALID, 373 NFP_IPSEC_CFG_MSG_SA_HASH_ADD_FAILED, 374 NFP_IPSEC_CFG_MSG_SA_HASH_DEL_FAILED, 375 NFP_IPSEC_CFG_MSG_SA_INVALID_CMD 376 }; 377 378 static int 379 nfp_ipsec_cfg_cmd_issue(struct nfp_net_hw *hw, 380 struct nfp_ipsec_msg *msg) 381 { 382 int ret; 383 uint32_t i; 384 uint32_t msg_size; 385 386 msg_size = RTE_DIM(msg->raw); 387 msg->rsp = NFP_IPSEC_CFG_MSG_OK; 388 389 for (i = 0; i < msg_size; i++) 390 nn_cfg_writel(hw, NFP_NET_CFG_MBOX_VAL + 4 * i, msg->raw[i]); 391 392 ret = nfp_net_mbox_reconfig(hw, NFP_NET_CFG_MBOX_CMD_IPSEC); 393 if (ret < 0) { 394 PMD_DRV_LOG(ERR, "Failed to IPsec reconfig mbox"); 395 return ret; 396 } 397 398 /* 399 * Not all commands and callers make use of response message data. But 400 * leave this up to the caller and always read and store the full 401 * response. One example where the data is needed is for statistics. 402 */ 403 for (i = 0; i < msg_size; i++) 404 msg->raw[i] = nn_cfg_readl(hw, NFP_NET_CFG_MBOX_VAL + 4 * i); 405 406 switch (msg->rsp) { 407 case NFP_IPSEC_CFG_MSG_OK: 408 ret = 0; 409 break; 410 case NFP_IPSEC_CFG_MSG_SA_INVALID_CMD: 411 ret = -EINVAL; 412 break; 413 case NFP_IPSEC_CFG_MSG_SA_VALID: 414 ret = -EEXIST; 415 break; 416 case NFP_IPSEC_CFG_MSG_FAILED: 417 /* FALLTHROUGH */ 418 case NFP_IPSEC_CFG_MSG_SA_HASH_ADD_FAILED: 419 /* FALLTHROUGH */ 420 case NFP_IPSEC_CFG_MSG_SA_HASH_DEL_FAILED: 421 ret = -EIO; 422 break; 423 default: 424 ret = -EDOM; 425 } 426 427 return ret; 428 } 429 430 /** 431 * Get discards packet statistics for each SA 432 * 433 * The sa_discard_stats contains the statistics of discards packets 434 * of an SA. This function calculates the sum total of discarded packets. 435 * 436 * @param errors 437 * The value is SA discards packet sum total 438 * @param sa_discard_stats 439 * The struct is SA discards packet Statistics 440 */ 441 static void 442 nfp_get_errorstats(uint64_t *errors, 443 struct ipsec_discard_stats *sa_discard_stats) 444 { 445 uint32_t i; 446 uint32_t len; 447 uint32_t *perror; 448 449 perror = &sa_discard_stats->discards_auth; 450 len = sizeof(struct ipsec_discard_stats) / sizeof(uint32_t); 451 452 for (i = 0; i < len; i++) 453 *errors += *perror++; 454 455 *errors -= sa_discard_stats->ipv4_id_counter; 456 } 457 458 static int 459 nfp_security_session_get_stats(void *device, 460 struct rte_security_session *session, 461 struct rte_security_stats *stats) 462 { 463 int ret; 464 struct nfp_net_hw *hw; 465 struct nfp_ipsec_msg msg; 466 struct rte_eth_dev *eth_dev; 467 struct ipsec_get_sa_stats *cfg_s; 468 struct rte_security_ipsec_stats *ips_s; 469 struct nfp_ipsec_session *priv_session; 470 enum rte_security_ipsec_sa_direction direction; 471 472 eth_dev = device; 473 priv_session = SECURITY_GET_SESS_PRIV(session); 474 memset(&msg, 0, sizeof(msg)); 475 msg.cmd = NFP_IPSEC_CFG_MSG_GET_SA_STATS; 476 msg.sa_idx = priv_session->sa_index; 477 hw = NFP_NET_DEV_PRIVATE_TO_HW(eth_dev->data->dev_private); 478 479 ret = nfp_ipsec_cfg_cmd_issue(hw, &msg); 480 if (ret < 0) { 481 PMD_DRV_LOG(ERR, "Failed to get SA stats"); 482 return ret; 483 } 484 485 cfg_s = &msg.cfg_get_stats; 486 direction = priv_session->ipsec.direction; 487 memset(stats, 0, sizeof(struct rte_security_stats)); /* Start with zeros */ 488 stats->protocol = RTE_SECURITY_PROTOCOL_IPSEC; 489 ips_s = &stats->ipsec; 490 491 /* Only display SA if any counters are non-zero */ 492 if (cfg_s->lifetime_byte_count != 0 || cfg_s->pkt_count != 0) { 493 if (direction == RTE_SECURITY_IPSEC_SA_DIR_INGRESS) { 494 ips_s->ipackets = cfg_s->pkt_count; 495 ips_s->ibytes = cfg_s->lifetime_byte_count; 496 nfp_get_errorstats(&ips_s->ierrors, &cfg_s->sa_discard_stats); 497 } else { 498 ips_s->opackets = cfg_s->pkt_count; 499 ips_s->obytes = cfg_s->lifetime_byte_count; 500 nfp_get_errorstats(&ips_s->oerrors, &cfg_s->sa_discard_stats); 501 } 502 } 503 504 return 0; 505 } 506 507 static const struct rte_security_capability * 508 nfp_crypto_capabilities_get(void *device __rte_unused) 509 { 510 return nfp_security_caps; 511 } 512 513 static uint32_t 514 nfp_security_session_get_size(void *device __rte_unused) 515 { 516 return sizeof(struct nfp_ipsec_session); 517 } 518 519 static const struct rte_security_ops nfp_security_ops = { 520 .session_get_size = nfp_security_session_get_size, 521 .session_stats_get = nfp_security_session_get_stats, 522 .capabilities_get = nfp_crypto_capabilities_get, 523 }; 524 525 static int 526 nfp_ipsec_ctx_create(struct rte_eth_dev *dev, 527 struct nfp_net_ipsec_data *data) 528 { 529 struct rte_security_ctx *ctx; 530 static const struct rte_mbuf_dynfield pkt_md_dynfield = { 531 .name = "nfp_ipsec_crypto_pkt_metadata", 532 .size = sizeof(struct nfp_tx_ipsec_desc_msg), 533 .align = __alignof__(struct nfp_tx_ipsec_desc_msg), 534 }; 535 536 ctx = rte_zmalloc("security_ctx", 537 sizeof(struct rte_security_ctx), 0); 538 if (ctx == NULL) { 539 PMD_INIT_LOG(ERR, "Failed to malloc security_ctx"); 540 return -ENOMEM; 541 } 542 543 ctx->device = dev; 544 ctx->ops = &nfp_security_ops; 545 ctx->sess_cnt = 0; 546 dev->security_ctx = ctx; 547 548 data->pkt_dynfield_offset = rte_mbuf_dynfield_register(&pkt_md_dynfield); 549 if (data->pkt_dynfield_offset < 0) { 550 PMD_INIT_LOG(ERR, "Failed to register mbuf esn_dynfield"); 551 return -ENOMEM; 552 } 553 554 return 0; 555 } 556 557 int 558 nfp_ipsec_init(struct rte_eth_dev *dev) 559 { 560 int ret; 561 uint32_t cap_extend; 562 struct nfp_net_hw *hw; 563 struct nfp_net_ipsec_data *data; 564 565 hw = NFP_NET_DEV_PRIVATE_TO_HW(dev->data->dev_private); 566 567 cap_extend = nn_cfg_readl(hw, NFP_NET_CFG_CAP_WORD1); 568 if ((cap_extend & NFP_NET_CFG_CTRL_IPSEC) == 0) { 569 PMD_INIT_LOG(INFO, "Unsupported IPsec extend capability"); 570 return 0; 571 } 572 573 data = rte_zmalloc("ipsec_data", sizeof(struct nfp_net_ipsec_data), 0); 574 if (data == NULL) { 575 PMD_INIT_LOG(ERR, "Failed to malloc ipsec_data"); 576 return -ENOMEM; 577 } 578 579 data->pkt_dynfield_offset = -1; 580 data->sa_free_cnt = NFP_NET_IPSEC_MAX_SA_CNT; 581 hw->ipsec_data = data; 582 583 ret = nfp_ipsec_ctx_create(dev, data); 584 if (ret != 0) { 585 PMD_INIT_LOG(ERR, "Failed to create IPsec ctx"); 586 goto ipsec_cleanup; 587 } 588 589 return 0; 590 591 ipsec_cleanup: 592 nfp_ipsec_uninit(dev); 593 594 return ret; 595 } 596 597 static void 598 nfp_ipsec_ctx_destroy(struct rte_eth_dev *dev) 599 { 600 if (dev->security_ctx != NULL) 601 rte_free(dev->security_ctx); 602 } 603 604 void 605 nfp_ipsec_uninit(struct rte_eth_dev *dev) 606 { 607 uint16_t i; 608 uint32_t cap_extend; 609 struct nfp_net_hw *hw; 610 struct nfp_ipsec_session *priv_session; 611 612 hw = NFP_NET_DEV_PRIVATE_TO_HW(dev->data->dev_private); 613 614 cap_extend = nn_cfg_readl(hw, NFP_NET_CFG_CAP_WORD1); 615 if ((cap_extend & NFP_NET_CFG_CTRL_IPSEC) == 0) { 616 PMD_INIT_LOG(INFO, "Unsupported IPsec extend capability"); 617 return; 618 } 619 620 nfp_ipsec_ctx_destroy(dev); 621 622 if (hw->ipsec_data == NULL) { 623 PMD_INIT_LOG(INFO, "IPsec data is NULL!"); 624 return; 625 } 626 627 for (i = 0; i < NFP_NET_IPSEC_MAX_SA_CNT; i++) { 628 priv_session = hw->ipsec_data->sa_entries[i]; 629 if (priv_session != NULL) 630 memset(priv_session, 0, sizeof(struct nfp_ipsec_session)); 631 } 632 633 rte_free(hw->ipsec_data); 634 } 635 636