X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=examples%2Fipsec-secgw%2Fsa.c;h=632482176caaa5b0619a1ea789422d3ebad688ac;hb=5ec3eb3b839ded5f0f04754721475433bf5d69df;hp=b850e9839e564582512768cfd0b9494504aaf7f9;hpb=49757b6845835394d1a9a6efad9356a6da58fd85;p=dpdk.git diff --git a/examples/ipsec-secgw/sa.c b/examples/ipsec-secgw/sa.c index b850e9839e..632482176c 100644 --- a/examples/ipsec-secgw/sa.c +++ b/examples/ipsec-secgw/sa.c @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: BSD-3-Clause - * Copyright(c) 2016-2017 Intel Corporation + * Copyright(c) 2016-2020 Intel Corporation */ /* @@ -24,9 +24,16 @@ #include "ipsec.h" #include "esp.h" #include "parser.h" +#include "sad.h" #define IPDEFTTL 64 +#define IP4_FULL_MASK (sizeof(((struct ip_addr *)NULL)->ip.ip4) * CHAR_BIT) + +#define IP6_FULL_MASK (sizeof(((struct ip_addr *)NULL)->ip.ip6.ip6) * CHAR_BIT) + +#define MBUF_NO_SEC_OFFLOAD(m) ((m->ol_flags & PKT_RX_SEC_OFFLOAD) == 0) + struct supported_cipher_algo { const char *keyword; enum rte_crypto_cipher_algorithm algo; @@ -69,6 +76,13 @@ const struct supported_cipher_algo cipher_algos[] = { .block_size = 16, .key_len = 16 }, + { + .keyword = "aes-192-cbc", + .algo = RTE_CRYPTO_CIPHER_AES_CBC, + .iv_len = 16, + .block_size = 16, + .key_len = 24 + }, { .keyword = "aes-256-cbc", .algo = RTE_CRYPTO_CIPHER_AES_CBC, @@ -109,7 +123,7 @@ const struct supported_auth_algo auth_algos[] = { { .keyword = "sha256-hmac", .algo = RTE_CRYPTO_AUTH_SHA256_HMAC, - .digest_len = 12, + .digest_len = 16, .key_len = 32 } }; @@ -123,14 +137,38 @@ const struct supported_aead_algo aead_algos[] = { .key_len = 20, .digest_len = 16, .aad_len = 8, + }, + { + .keyword = "aes-192-gcm", + .algo = RTE_CRYPTO_AEAD_AES_GCM, + .iv_len = 8, + .block_size = 4, + .key_len = 28, + .digest_len = 16, + .aad_len = 8, + }, + { + .keyword = "aes-256-gcm", + .algo = RTE_CRYPTO_AEAD_AES_GCM, + .iv_len = 8, + .block_size = 4, + .key_len = 36, + .digest_len = 16, + .aad_len = 8, } }; -static struct ipsec_sa sa_out[IPSEC_SA_MAX_ENTRIES]; -static uint32_t nb_sa_out; +#define SA_INIT_NB 128 + +struct ipsec_sa *sa_out; +uint32_t nb_sa_out; +static uint32_t sa_out_sz; +static struct ipsec_sa_cnt sa_out_cnt; -static struct ipsec_sa sa_in[IPSEC_SA_MAX_ENTRIES]; -static uint32_t nb_sa_in; +struct ipsec_sa *sa_in; +uint32_t nb_sa_in; +static uint32_t sa_in_sz; +static struct ipsec_sa_cnt sa_in_cnt; static const struct supported_cipher_algo * find_match_cipher_algo(const char *cipher_keyword) @@ -215,13 +253,40 @@ parse_key_string(const char *key_str, uint8_t *key) return nb_bytes; } +static int +extend_sa_arr(struct ipsec_sa **sa_tbl, uint32_t cur_cnt, uint32_t *cur_sz) +{ + if (*sa_tbl == NULL) { + *sa_tbl = calloc(SA_INIT_NB, sizeof(struct ipsec_sa)); + if (*sa_tbl == NULL) + return -1; + *cur_sz = SA_INIT_NB; + return 0; + } + + if (cur_cnt >= *cur_sz) { + *sa_tbl = realloc(*sa_tbl, + *cur_sz * sizeof(struct ipsec_sa) * 2); + if (*sa_tbl == NULL) + return -1; + /* clean reallocated extra space */ + memset(&(*sa_tbl)[*cur_sz], 0, + *cur_sz * sizeof(struct ipsec_sa)); + *cur_sz *= 2; + } + + return 0; +} + void parse_sa_tokens(char **tokens, uint32_t n_tokens, struct parse_status *status) { struct ipsec_sa *rule = NULL; + struct rte_ipsec_session *ips; uint32_t ti; /*token index*/ uint32_t *ri /*rule index*/; + struct ipsec_sa_cnt *sa_cnt; uint32_t cipher_algo_p = 0; uint32_t auth_algo_p = 0; uint32_t aead_algo_p = 0; @@ -230,25 +295,23 @@ parse_sa_tokens(char **tokens, uint32_t n_tokens, uint32_t mode_p = 0; uint32_t type_p = 0; uint32_t portid_p = 0; + uint32_t fallback_p = 0; + int16_t status_p = 0; if (strcmp(tokens[0], "in") == 0) { ri = &nb_sa_in; - - APP_CHECK(*ri <= IPSEC_SA_MAX_ENTRIES - 1, status, - "too many sa rules, abort insertion\n"); - if (status->status < 0) + sa_cnt = &sa_in_cnt; + if (extend_sa_arr(&sa_in, nb_sa_in, &sa_in_sz) < 0) return; - rule = &sa_in[*ri]; + rule->direction = RTE_SECURITY_IPSEC_SA_DIR_INGRESS; } else { ri = &nb_sa_out; - - APP_CHECK(*ri <= IPSEC_SA_MAX_ENTRIES - 1, status, - "too many sa rules, abort insertion\n"); - if (status->status < 0) + sa_cnt = &sa_out_cnt; + if (extend_sa_arr(&sa_out, nb_sa_out, &sa_out_sz) < 0) return; - rule = &sa_out[*ri]; + rule->direction = RTE_SECURITY_IPSEC_SA_DIR_EGRESS; } /* spi number */ @@ -258,6 +321,8 @@ parse_sa_tokens(char **tokens, uint32_t n_tokens, if (atoi(tokens[1]) == INVALID_SPI) return; rule->spi = atoi(tokens[1]); + rule->portid = UINT16_MAX; + ips = ipsec_get_primary_session(rule); for (ti = 2; ti < n_tokens; ti++) { if (strcmp(tokens[ti], "mode") == 0) { @@ -269,13 +334,17 @@ parse_sa_tokens(char **tokens, uint32_t n_tokens, if (status->status < 0) return; - if (strcmp(tokens[ti], "ipv4-tunnel") == 0) + if (strcmp(tokens[ti], "ipv4-tunnel") == 0) { + sa_cnt->nb_v4++; rule->flags = IP4_TUNNEL; - else if (strcmp(tokens[ti], "ipv6-tunnel") == 0) + } else if (strcmp(tokens[ti], "ipv6-tunnel") == 0) { + sa_cnt->nb_v6++; rule->flags = IP6_TUNNEL; - else if (strcmp(tokens[ti], "transport") == 0) + } else if (strcmp(tokens[ti], "transport") == 0) { + sa_cnt->nb_v4++; + sa_cnt->nb_v6++; rule->flags = TRANSPORT; - else { + } else { APP_CHECK(0, status, "unrecognized " "input \"%s\"", tokens[ti]); return; @@ -303,6 +372,9 @@ parse_sa_tokens(char **tokens, uint32_t n_tokens, APP_CHECK(algo != NULL, status, "unrecognized " "input \"%s\"", tokens[ti]); + if (status->status < 0) + return; + rule->cipher_algo = algo->algo; rule->block_size = algo->block_size; rule->iv_len = algo->iv_len; @@ -367,6 +439,9 @@ parse_sa_tokens(char **tokens, uint32_t n_tokens, APP_CHECK(algo != NULL, status, "unrecognized " "input \"%s\"", tokens[ti]); + if (status->status < 0) + return; + rule->auth_algo = algo->algo; rule->auth_key_len = algo->key_len; rule->digest_len = algo->digest_len; @@ -422,6 +497,9 @@ parse_sa_tokens(char **tokens, uint32_t n_tokens, APP_CHECK(algo != NULL, status, "unrecognized " "input \"%s\"", tokens[ti]); + if (status->status < 0) + return; + rule->aead_algo = algo->algo; rule->cipher_key_len = algo->key_len; rule->digest_len = algo->digest_len; @@ -468,7 +546,7 @@ parse_sa_tokens(char **tokens, uint32_t n_tokens, if (status->status < 0) return; - if (rule->flags == IP4_TUNNEL) { + if (IS_IP4_TUNNEL(rule->flags)) { struct in_addr ip; APP_CHECK(parse_ipv4_addr(tokens[ti], @@ -480,7 +558,7 @@ parse_sa_tokens(char **tokens, uint32_t n_tokens, return; rule->src.ip.ip4 = rte_bswap32( (uint32_t)ip.s_addr); - } else if (rule->flags == IP6_TUNNEL) { + } else if (IS_IP6_TUNNEL(rule->flags)) { struct in6_addr ip; APP_CHECK(parse_ipv6_addr(tokens[ti], &ip, @@ -492,7 +570,7 @@ parse_sa_tokens(char **tokens, uint32_t n_tokens, return; memcpy(rule->src.ip.ip6.ip6_b, ip.s6_addr, 16); - } else if (rule->flags == TRANSPORT) { + } else if (IS_TRANSPORT(rule->flags)) { APP_CHECK(0, status, "unrecognized input " "\"%s\"", tokens[ti]); return; @@ -511,7 +589,7 @@ parse_sa_tokens(char **tokens, uint32_t n_tokens, if (status->status < 0) return; - if (rule->flags == IP4_TUNNEL) { + if (IS_IP4_TUNNEL(rule->flags)) { struct in_addr ip; APP_CHECK(parse_ipv4_addr(tokens[ti], @@ -523,7 +601,7 @@ parse_sa_tokens(char **tokens, uint32_t n_tokens, return; rule->dst.ip.ip4 = rte_bswap32( (uint32_t)ip.s_addr); - } else if (rule->flags == IP6_TUNNEL) { + } else if (IS_IP6_TUNNEL(rule->flags)) { struct in6_addr ip; APP_CHECK(parse_ipv6_addr(tokens[ti], &ip, @@ -534,7 +612,7 @@ parse_sa_tokens(char **tokens, uint32_t n_tokens, if (status->status < 0) return; memcpy(rule->dst.ip.ip6.ip6_b, ip.s6_addr, 16); - } else if (rule->flags == TRANSPORT) { + } else if (IS_TRANSPORT(rule->flags)) { APP_CHECK(0, status, "unrecognized " "input \"%s\"", tokens[ti]); return; @@ -554,18 +632,20 @@ parse_sa_tokens(char **tokens, uint32_t n_tokens, return; if (strcmp(tokens[ti], "inline-crypto-offload") == 0) - rule->type = + ips->type = RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO; else if (strcmp(tokens[ti], "inline-protocol-offload") == 0) - rule->type = + ips->type = RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL; else if (strcmp(tokens[ti], "lookaside-protocol-offload") == 0) - rule->type = + ips->type = RTE_SECURITY_ACTION_TYPE_LOOKASIDE_PROTOCOL; else if (strcmp(tokens[ti], "no-offload") == 0) - rule->type = RTE_SECURITY_ACTION_TYPE_NONE; + ips->type = RTE_SECURITY_ACTION_TYPE_NONE; + else if (strcmp(tokens[ti], "cpu-crypto") == 0) + ips->type = RTE_SECURITY_ACTION_TYPE_CPU_CRYPTO; else { APP_CHECK(0, status, "Invalid input \"%s\"", tokens[ti]); @@ -583,13 +663,99 @@ parse_sa_tokens(char **tokens, uint32_t n_tokens, INCREMENT_TOKEN_INDEX(ti, n_tokens, status); if (status->status < 0) return; - rule->portid = atoi(tokens[ti]); - if (status->status < 0) + if (rule->portid == UINT16_MAX) + rule->portid = atoi(tokens[ti]); + else if (rule->portid != atoi(tokens[ti])) { + APP_CHECK(0, status, + "portid %s not matching with already assigned portid %u", + tokens[ti], rule->portid); return; + } portid_p = 1; continue; } + if (strcmp(tokens[ti], "fallback") == 0) { + struct rte_ipsec_session *fb; + + APP_CHECK(app_sa_prm.enable, status, "Fallback session " + "not allowed for legacy mode."); + if (status->status < 0) + return; + APP_CHECK(ips->type == + RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO, status, + "Fallback session allowed if primary session " + "is of type inline-crypto-offload only."); + if (status->status < 0) + return; + APP_CHECK(rule->direction == + RTE_SECURITY_IPSEC_SA_DIR_INGRESS, status, + "Fallback session not allowed for egress " + "rule"); + if (status->status < 0) + return; + APP_CHECK_PRESENCE(fallback_p, tokens[ti], status); + if (status->status < 0) + return; + INCREMENT_TOKEN_INDEX(ti, n_tokens, status); + if (status->status < 0) + return; + fb = ipsec_get_fallback_session(rule); + if (strcmp(tokens[ti], "lookaside-none") == 0) + fb->type = RTE_SECURITY_ACTION_TYPE_NONE; + else if (strcmp(tokens[ti], "cpu-crypto") == 0) + fb->type = RTE_SECURITY_ACTION_TYPE_CPU_CRYPTO; + else { + APP_CHECK(0, status, "unrecognized fallback " + "type %s.", tokens[ti]); + return; + } + + rule->fallback_sessions = 1; + fallback_p = 1; + continue; + } + if (strcmp(tokens[ti], "flow-direction") == 0) { + switch (ips->type) { + case RTE_SECURITY_ACTION_TYPE_NONE: + case RTE_SECURITY_ACTION_TYPE_CPU_CRYPTO: + rule->fdir_flag = 1; + INCREMENT_TOKEN_INDEX(ti, n_tokens, status); + if (status->status < 0) + return; + if (rule->portid == UINT16_MAX) + rule->portid = atoi(tokens[ti]); + else if (rule->portid != atoi(tokens[ti])) { + APP_CHECK(0, status, + "portid %s not matching with already assigned portid %u", + tokens[ti], rule->portid); + return; + } + INCREMENT_TOKEN_INDEX(ti, n_tokens, status); + if (status->status < 0) + return; + rule->fdir_qid = atoi(tokens[ti]); + /* validating portid and queueid */ + status_p = check_flow_params(rule->portid, + rule->fdir_qid); + if (status_p < 0) { + printf("port id %u / queue id %u is " + "not valid\n", rule->portid, + rule->fdir_qid); + } + break; + case RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO: + case RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL: + case RTE_SECURITY_ACTION_TYPE_LOOKASIDE_PROTOCOL: + default: + APP_CHECK(0, status, + "flow director not supported for security session type %d", + ips->type); + return; + } + continue; + } + /* unrecognizeable input */ APP_CHECK(0, status, "unrecognized input \"%s\"", tokens[ti]); @@ -620,12 +786,13 @@ parse_sa_tokens(char **tokens, uint32_t n_tokens, if (status->status < 0) return; - if ((rule->type != RTE_SECURITY_ACTION_TYPE_NONE) && (portid_p == 0)) + if ((ips->type != RTE_SECURITY_ACTION_TYPE_NONE && ips->type != + RTE_SECURITY_ACTION_TYPE_CPU_CRYPTO) && (portid_p == 0)) printf("Missing portid option, falling back to non-offload\n"); - if (!type_p || !portid_p) { - rule->type = RTE_SECURITY_ACTION_TYPE_NONE; - rule->portid = -1; + if (!type_p || (!portid_p && ips->type != + RTE_SECURITY_ACTION_TYPE_CPU_CRYPTO)) { + ips->type = RTE_SECURITY_ACTION_TYPE_NONE; } *ri = *ri + 1; @@ -636,6 +803,8 @@ print_one_sa_rule(const struct ipsec_sa *sa, int inbound) { uint32_t i; uint8_t a, b, c, d; + const struct rte_ipsec_session *ips; + const struct rte_ipsec_session *fallback_ips; printf("\tspi_%s(%3u):", inbound?"in":"out", sa->spi); @@ -655,7 +824,8 @@ print_one_sa_rule(const struct ipsec_sa *sa, int inbound) } for (i = 0; i < RTE_DIM(aead_algos); i++) { - if (aead_algos[i].algo == sa->aead_algo) { + if (aead_algos[i].algo == sa->aead_algo && + aead_algos[i].key_len-4 == sa->cipher_key_len) { printf("%s ", aead_algos[i].keyword); break; } @@ -663,7 +833,7 @@ print_one_sa_rule(const struct ipsec_sa *sa, int inbound) printf("mode:"); - switch (sa->flags) { + switch (WITHOUT_TRANSPORT_VERSION(sa->flags)) { case IP4_TUNNEL: printf("IP4Tunnel "); uint32_t_to_char(sa->src.ip.ip4, &a, &b, &c, &d); @@ -691,8 +861,10 @@ print_one_sa_rule(const struct ipsec_sa *sa, int inbound) printf("Transport "); break; } + + ips = &sa->sessions[IPSEC_SESSION_PRIMARY]; printf(" type:"); - switch (sa->type) { + switch (ips->type) { case RTE_SECURITY_ACTION_TYPE_NONE: printf("no-offload "); break; @@ -705,23 +877,35 @@ print_one_sa_rule(const struct ipsec_sa *sa, int inbound) case RTE_SECURITY_ACTION_TYPE_LOOKASIDE_PROTOCOL: printf("lookaside-protocol-offload "); break; + case RTE_SECURITY_ACTION_TYPE_CPU_CRYPTO: + printf("cpu-crypto-accelerated "); + break; } + + fallback_ips = &sa->sessions[IPSEC_SESSION_FALLBACK]; + if (fallback_ips != NULL && sa->fallback_sessions > 0) { + printf("inline fallback: "); + switch (fallback_ips->type) { + case RTE_SECURITY_ACTION_TYPE_NONE: + printf("lookaside-none"); + break; + case RTE_SECURITY_ACTION_TYPE_CPU_CRYPTO: + printf("cpu-crypto-accelerated"); + break; + default: + printf("invalid"); + break; + } + } + if (sa->fdir_flag == 1) + printf("flow-direction port %d queue %d", sa->portid, + sa->fdir_qid); + printf("\n"); } -struct sa_ctx { - void *satbl; /* pointer to array of rte_ipsec_sa objects*/ - struct ipsec_sa sa[IPSEC_SA_MAX_ENTRIES]; - union { - struct { - struct rte_crypto_sym_xform a; - struct rte_crypto_sym_xform b; - }; - } xf[IPSEC_SA_MAX_ENTRIES]; -}; - static struct sa_ctx * -sa_create(const char *name, int32_t socket_id) +sa_create(const char *name, int32_t socket_id, uint32_t nb_sa) { char s[PATH_MAX]; struct sa_ctx *sa_ctx; @@ -730,20 +914,31 @@ sa_create(const char *name, int32_t socket_id) snprintf(s, sizeof(s), "%s_%u", name, socket_id); - /* Create SA array table */ + /* Create SA context */ printf("Creating SA context with %u maximum entries on socket %d\n", - IPSEC_SA_MAX_ENTRIES, socket_id); + nb_sa, socket_id); - mz_size = sizeof(struct sa_ctx); + mz_size = sizeof(struct ipsec_xf) * nb_sa; mz = rte_memzone_reserve(s, mz_size, socket_id, RTE_MEMZONE_1GB | RTE_MEMZONE_SIZE_HINT_ONLY); if (mz == NULL) { - printf("Failed to allocate SA DB memory\n"); - rte_errno = -ENOMEM; + printf("Failed to allocate SA XFORM memory\n"); + rte_errno = ENOMEM; + return NULL; + } + + sa_ctx = rte_zmalloc(NULL, sizeof(struct sa_ctx) + + sizeof(struct ipsec_sa) * nb_sa, RTE_CACHE_LINE_SIZE); + + if (sa_ctx == NULL) { + printf("Failed to allocate SA CTX memory\n"); + rte_errno = ENOMEM; + rte_memzone_free(mz); return NULL; } - sa_ctx = (struct sa_ctx *)mz->addr; + sa_ctx->xf = (struct ipsec_xf *)mz->addr; + sa_ctx->nb_sa = nb_sa; return sa_ctx; } @@ -752,8 +947,16 @@ static int check_eth_dev_caps(uint16_t portid, uint32_t inbound) { struct rte_eth_dev_info dev_info; + int retval; - rte_eth_dev_info_get(portid, &dev_info); + retval = rte_eth_dev_info_get(portid, &dev_info); + if (retval != 0) { + RTE_LOG(ERR, IPSEC, + "Error during getting device (port %u) info: %s\n", + portid, strerror(-retval)); + + return retval; + } if (inbound) { if ((dev_info.rx_offload_capa & @@ -774,20 +977,111 @@ check_eth_dev_caps(uint16_t portid, uint32_t inbound) return 0; } +/* + * Helper function, tries to determine next_proto for SPI + * by searching though SP rules. + */ +static int +get_spi_proto(uint32_t spi, enum rte_security_ipsec_sa_direction dir, + struct ip_addr ip_addr[2], uint32_t mask[2]) +{ + int32_t rc4, rc6; + + rc4 = sp4_spi_present(spi, dir == RTE_SECURITY_IPSEC_SA_DIR_INGRESS, + ip_addr, mask); + rc6 = sp6_spi_present(spi, dir == RTE_SECURITY_IPSEC_SA_DIR_INGRESS, + ip_addr, mask); + + if (rc4 >= 0) { + if (rc6 >= 0) { + RTE_LOG(ERR, IPSEC, + "%s: SPI %u used simultaeously by " + "IPv4(%d) and IPv6 (%d) SP rules\n", + __func__, spi, rc4, rc6); + return -EINVAL; + } else + return IPPROTO_IPIP; + } else if (rc6 < 0) { + RTE_LOG(ERR, IPSEC, + "%s: SPI %u is not used by any SP rule\n", + __func__, spi); + return -EINVAL; + } else + return IPPROTO_IPV6; +} + +/* + * Helper function for getting source and destination IP addresses + * from SP. Needed for inline crypto transport mode, as addresses are not + * provided in config file for that mode. It checks if SP for current SA exists, + * and based on what type of protocol is returned, it stores appropriate + * addresses got from SP into SA. + */ +static int +sa_add_address_inline_crypto(struct ipsec_sa *sa) +{ + int protocol; + struct ip_addr ip_addr[2]; + uint32_t mask[2]; + + protocol = get_spi_proto(sa->spi, sa->direction, ip_addr, mask); + if (protocol < 0) + return protocol; + else if (protocol == IPPROTO_IPIP) { + sa->flags |= IP4_TRANSPORT; + if (mask[0] == IP4_FULL_MASK && + mask[1] == IP4_FULL_MASK && + ip_addr[0].ip.ip4 != 0 && + ip_addr[1].ip.ip4 != 0) { + + sa->src.ip.ip4 = ip_addr[0].ip.ip4; + sa->dst.ip.ip4 = ip_addr[1].ip.ip4; + } else { + RTE_LOG(ERR, IPSEC, + "%s: No valid address or mask entry in" + " IPv4 SP rule for SPI %u\n", + __func__, sa->spi); + return -EINVAL; + } + } else if (protocol == IPPROTO_IPV6) { + sa->flags |= IP6_TRANSPORT; + if (mask[0] == IP6_FULL_MASK && + mask[1] == IP6_FULL_MASK && + (ip_addr[0].ip.ip6.ip6[0] != 0 || + ip_addr[0].ip.ip6.ip6[1] != 0) && + (ip_addr[1].ip.ip6.ip6[0] != 0 || + ip_addr[1].ip.ip6.ip6[1] != 0)) { + + sa->src.ip.ip6 = ip_addr[0].ip.ip6; + sa->dst.ip.ip6 = ip_addr[1].ip.ip6; + } else { + RTE_LOG(ERR, IPSEC, + "%s: No valid address or mask entry in" + " IPv6 SP rule for SPI %u\n", + __func__, sa->spi); + return -EINVAL; + } + } + return 0; +} static int sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[], - uint32_t nb_entries, uint32_t inbound) + uint32_t nb_entries, uint32_t inbound, + struct socket_ctx *skt_ctx) { struct ipsec_sa *sa; uint32_t i, idx; uint16_t iv_length, aad_length; + int inline_status; + int32_t rc; + struct rte_ipsec_session *ips; /* for ESN upper 32 bits of SQN also need to be part of AAD */ aad_length = (app_sa_prm.enable_esn != 0) ? sizeof(uint32_t) : 0; for (i = 0; i < nb_entries; i++) { - idx = SPI2IDX(entries[i].spi); + idx = i; sa = &sa_ctx->sa[idx]; if (sa->spi != 0) { printf("Index %u already in use by SPI %u\n", @@ -795,26 +1089,40 @@ sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[], return -EINVAL; } *sa = entries[i]; + + if (inbound) { + rc = ipsec_sad_add(&sa_ctx->sad, sa); + if (rc != 0) + return rc; + } + sa->seq = 0; + ips = ipsec_get_primary_session(sa); - if (sa->type == RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL || - sa->type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO) { + if (ips->type == RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL || + ips->type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO) { if (check_eth_dev_caps(sa->portid, inbound)) return -EINVAL; } - sa->direction = (inbound == 1) ? - RTE_SECURITY_IPSEC_SA_DIR_INGRESS : - RTE_SECURITY_IPSEC_SA_DIR_EGRESS; - - switch (sa->flags) { + switch (WITHOUT_TRANSPORT_VERSION(sa->flags)) { case IP4_TUNNEL: sa->src.ip.ip4 = rte_cpu_to_be_32(sa->src.ip.ip4); sa->dst.ip.ip4 = rte_cpu_to_be_32(sa->dst.ip.ip4); + break; + case TRANSPORT: + if (ips->type == + RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO) { + inline_status = + sa_add_address_inline_crypto(sa); + if (inline_status < 0) + return inline_status; + } + break; } if (sa->aead_algo == RTE_CRYPTO_AEAD_AES_GCM) { - iv_length = 16; + iv_length = 12; sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_AEAD; sa_ctx->xf[idx].a.aead.algo = sa->aead_algo; @@ -833,8 +1141,6 @@ sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[], sa->digest_len; sa->xforms = &sa_ctx->xf[idx].a; - - print_one_sa_rule(sa, inbound); } else { switch (sa->cipher_algo) { case RTE_CRYPTO_CIPHER_NULL: @@ -899,9 +1205,27 @@ sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[], sa_ctx->xf[idx].a.next = &sa_ctx->xf[idx].b; sa_ctx->xf[idx].b.next = NULL; sa->xforms = &sa_ctx->xf[idx].a; + } + + if (ips->type == + RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL || + ips->type == + RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO) { + rc = create_inline_session(skt_ctx, sa, ips); + if (rc != 0) { + RTE_LOG(ERR, IPSEC_ESP, + "create_inline_session() failed\n"); + return -EINVAL; + } + } - print_one_sa_rule(sa, inbound); + if (sa->fdir_flag && inbound) { + rc = create_ipsec_esp_flow(sa); + if (rc != 0) + RTE_LOG(ERR, IPSEC_ESP, + "create_ipsec_esp_flow() failed\n"); } + print_one_sa_rule(sa, inbound); } return 0; @@ -909,16 +1233,16 @@ sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[], static inline int sa_out_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[], - uint32_t nb_entries) + uint32_t nb_entries, struct socket_ctx *skt_ctx) { - return sa_add_rules(sa_ctx, entries, nb_entries, 0); + return sa_add_rules(sa_ctx, entries, nb_entries, 0, skt_ctx); } static inline int sa_in_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[], - uint32_t nb_entries) + uint32_t nb_entries, struct socket_ctx *skt_ctx) { - return sa_add_rules(sa_ctx, entries, nb_entries, 1); + return sa_add_rules(sa_ctx, entries, nb_entries, 1, skt_ctx); } /* @@ -932,42 +1256,12 @@ fill_ipsec_app_sa_prm(struct rte_ipsec_sa_prm *prm, prm->flags = app_prm->flags; prm->ipsec_xform.options.esn = app_prm->enable_esn; - prm->replay_win_sz = app_prm->window_size; -} - -/* - * Helper function, tries to determine next_proto for SPI - * by searching though SP rules. - */ -static int -get_spi_proto(uint32_t spi, enum rte_security_ipsec_sa_direction dir) -{ - int32_t rc4, rc6; - - rc4 = sp4_spi_present(spi, dir == RTE_SECURITY_IPSEC_SA_DIR_INGRESS); - rc6 = sp6_spi_present(spi, dir == RTE_SECURITY_IPSEC_SA_DIR_INGRESS); - - if (rc4 >= 0) { - if (rc6 >= 0) { - RTE_LOG(ERR, IPSEC, - "%s: SPI %u used simultaeously by " - "IPv4(%d) and IPv6 (%d) SP rules\n", - __func__, spi, rc4, rc6); - return -EINVAL; - } else - return IPPROTO_IPIP; - } else if (rc6 < 0) { - RTE_LOG(ERR, IPSEC, - "%s: SPI %u is not used by any SP rule\n", - __func__, spi); - return -EINVAL; - } else - return IPPROTO_IPV6; + prm->ipsec_xform.replay_win_sz = app_prm->window_size; } static int fill_ipsec_sa_prm(struct rte_ipsec_sa_prm *prm, const struct ipsec_sa *ss, - const struct ipv4_hdr *v4, struct ipv6_hdr *v6) + const struct rte_ipv4_hdr *v4, struct rte_ipv6_hdr *v6) { int32_t rc; @@ -976,7 +1270,7 @@ fill_ipsec_sa_prm(struct rte_ipsec_sa_prm *prm, const struct ipsec_sa *ss, * probably not the optimal way, but there seems nothing * better right now. */ - rc = get_spi_proto(ss->spi, ss->direction); + rc = get_spi_proto(ss->spi, ss->direction, NULL, NULL); if (rc < 0) return rc; @@ -988,16 +1282,18 @@ fill_ipsec_sa_prm(struct rte_ipsec_sa_prm *prm, const struct ipsec_sa *ss, prm->ipsec_xform.salt = ss->salt; prm->ipsec_xform.direction = ss->direction; prm->ipsec_xform.proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP; - prm->ipsec_xform.mode = (ss->flags == TRANSPORT) ? + prm->ipsec_xform.mode = (IS_TRANSPORT(ss->flags)) ? RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT : RTE_SECURITY_IPSEC_SA_MODE_TUNNEL; + prm->ipsec_xform.options.ecn = 1; + prm->ipsec_xform.options.copy_dscp = 1; - if (ss->flags == IP4_TUNNEL) { + if (IS_IP4_TUNNEL(ss->flags)) { prm->ipsec_xform.tunnel.type = RTE_SECURITY_IPSEC_TUNNEL_IPV4; prm->tun.hdr_len = sizeof(*v4); prm->tun.next_proto = rc; prm->tun.hdr = v4; - } else if (ss->flags == IP6_TUNNEL) { + } else if (IS_IP6_TUNNEL(ss->flags)) { prm->ipsec_xform.tunnel.type = RTE_SECURITY_IPSEC_TUNNEL_IPV6; prm->tun.hdr_len = sizeof(*v6); prm->tun.next_proto = rc; @@ -1012,22 +1308,23 @@ fill_ipsec_sa_prm(struct rte_ipsec_sa_prm *prm, const struct ipsec_sa *ss, return 0; } -static void -fill_ipsec_session(struct rte_ipsec_session *ss, struct rte_ipsec_sa *sa, - const struct ipsec_sa *lsa) +static int +fill_ipsec_session(struct rte_ipsec_session *ss, struct rte_ipsec_sa *sa) { + int32_t rc = 0; + ss->sa = sa; - ss->type = lsa->type; - /* setup crypto section */ - if (ss->type == RTE_SECURITY_ACTION_TYPE_NONE) { - ss->crypto.ses = lsa->crypto_session; - /* setup session action type */ - } else { - ss->security.ses = lsa->sec_session; - ss->security.ctx = lsa->security_ctx; - ss->security.ol_flags = lsa->ol_flags; + if (ss->type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO || + ss->type == RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL) { + if (ss->security.ses != NULL) { + rc = rte_ipsec_session_prepare(ss); + if (rc != 0) + memset(ss, 0, sizeof(*ss)); + } } + + return rc; } /* @@ -1038,20 +1335,21 @@ ipsec_sa_init(struct ipsec_sa *lsa, struct rte_ipsec_sa *sa, uint32_t sa_size) { int rc; struct rte_ipsec_sa_prm prm; - struct ipv4_hdr v4 = { + struct rte_ipsec_session *ips; + struct rte_ipv4_hdr v4 = { .version_ihl = IPVERSION << 4 | - sizeof(v4) / IPV4_IHL_MULTIPLIER, + sizeof(v4) / RTE_IPV4_IHL_MULTIPLIER, .time_to_live = IPDEFTTL, .next_proto_id = IPPROTO_ESP, .src_addr = lsa->src.ip.ip4, .dst_addr = lsa->dst.ip.ip4, }; - struct ipv6_hdr v6 = { + struct rte_ipv6_hdr v6 = { .vtc_flow = htonl(IP6_VERSION << 28), .proto = IPPROTO_ESP, }; - if (lsa->flags == IP6_TUNNEL) { + if (IS_IP6_TUNNEL(lsa->flags)) { memcpy(v6.src_addr, lsa->src.ip.ip6.ip6_b, sizeof(v6.src_addr)); memcpy(v6.dst_addr, lsa->dst.ip.ip6.ip6_b, sizeof(v6.dst_addr)); } @@ -1062,8 +1360,17 @@ ipsec_sa_init(struct ipsec_sa *lsa, struct rte_ipsec_sa *sa, uint32_t sa_size) if (rc < 0) return rc; - fill_ipsec_session(&lsa->ips, sa, lsa); - return 0; + /* init primary processing session */ + ips = ipsec_get_primary_session(lsa); + rc = fill_ipsec_session(ips, sa); + if (rc != 0) + return rc; + + /* init inline fallback processing session */ + if (lsa->fallback_sessions == 1) + rc = fill_ipsec_session(ipsec_get_fallback_session(lsa), sa); + + return rc; } /* @@ -1071,8 +1378,7 @@ ipsec_sa_init(struct ipsec_sa *lsa, struct rte_ipsec_sa *sa, uint32_t sa_size) * one per session. */ static int -ipsec_satbl_init(struct sa_ctx *ctx, const struct ipsec_sa *ent, - uint32_t nb_ent, int32_t socket) +ipsec_satbl_init(struct sa_ctx *ctx, uint32_t nb_ent, int32_t socket) { int32_t rc, sz; uint32_t i, idx; @@ -1082,7 +1388,7 @@ ipsec_satbl_init(struct sa_ctx *ctx, const struct ipsec_sa *ent, struct rte_ipsec_sa_prm prm; /* determine SA size */ - idx = SPI2IDX(ent[0].spi); + idx = 0; fill_ipsec_sa_prm(&prm, ctx->sa + idx, NULL, NULL); sz = rte_ipsec_sa_size(&prm); if (sz < 0) { @@ -1105,7 +1411,7 @@ ipsec_satbl_init(struct sa_ctx *ctx, const struct ipsec_sa *ent, rc = 0; for (i = 0; i != nb_ent && rc == 0; i++) { - idx = SPI2IDX(ent[i].spi); + idx = i; sa = (struct rte_ipsec_sa *)((uintptr_t)ctx->satbl + sz * i); lsa = ctx->sa + idx; @@ -1116,27 +1422,37 @@ ipsec_satbl_init(struct sa_ctx *ctx, const struct ipsec_sa *ent, return rc; } +static int +sa_cmp(const void *p, const void *q) +{ + uint32_t spi1 = ((const struct ipsec_sa *)p)->spi; + uint32_t spi2 = ((const struct ipsec_sa *)q)->spi; + + return (int)(spi1 - spi2); +} + /* * Walk through all SA rules to find an SA with given SPI */ int -sa_spi_present(uint32_t spi, int inbound) +sa_spi_present(struct sa_ctx *sa_ctx, uint32_t spi, int inbound) { - uint32_t i, num; + uint32_t num; + struct ipsec_sa *sa; + struct ipsec_sa tmpl; const struct ipsec_sa *sar; - if (inbound != 0) { - sar = sa_in; + sar = sa_ctx->sa; + if (inbound != 0) num = nb_sa_in; - } else { - sar = sa_out; + else num = nb_sa_out; - } - for (i = 0; i != num; i++) { - if (sar[i].spi == spi) - return i; - } + tmpl.spi = spi; + + sa = bsearch(&tmpl, sar, num, sizeof(struct ipsec_sa), sa_cmp); + if (sa != NULL) + return RTE_PTR_DIFF(sa, sar) / sizeof(struct ipsec_sa); return -ENOENT; } @@ -1160,16 +1476,21 @@ sa_init(struct socket_ctx *ctx, int32_t socket_id) if (nb_sa_in > 0) { name = "sa_in"; - ctx->sa_in = sa_create(name, socket_id); + ctx->sa_in = sa_create(name, socket_id, nb_sa_in); if (ctx->sa_in == NULL) rte_exit(EXIT_FAILURE, "Error [%d] creating SA " "context %s in socket %d\n", rte_errno, name, socket_id); - sa_in_add_rules(ctx->sa_in, sa_in, nb_sa_in); + rc = ipsec_sad_create(name, &ctx->sa_in->sad, socket_id, + &sa_in_cnt); + if (rc != 0) + rte_exit(EXIT_FAILURE, "failed to init SAD\n"); + + sa_in_add_rules(ctx->sa_in, sa_in, nb_sa_in, ctx); if (app_sa_prm.enable != 0) { - rc = ipsec_satbl_init(ctx->sa_in, sa_in, nb_sa_in, + rc = ipsec_satbl_init(ctx->sa_in, nb_sa_in, socket_id); if (rc != 0) rte_exit(EXIT_FAILURE, @@ -1180,16 +1501,16 @@ sa_init(struct socket_ctx *ctx, int32_t socket_id) if (nb_sa_out > 0) { name = "sa_out"; - ctx->sa_out = sa_create(name, socket_id); + ctx->sa_out = sa_create(name, socket_id, nb_sa_out); if (ctx->sa_out == NULL) rte_exit(EXIT_FAILURE, "Error [%d] creating SA " "context %s in socket %d\n", rte_errno, name, socket_id); - sa_out_add_rules(ctx->sa_out, sa_out, nb_sa_out); + sa_out_add_rules(ctx->sa_out, sa_out, nb_sa_out, ctx); if (app_sa_prm.enable != 0) { - rc = ipsec_satbl_init(ctx->sa_out, sa_out, nb_sa_out, + rc = ipsec_satbl_init(ctx->sa_out, nb_sa_out, socket_id); if (rc != 0) rte_exit(EXIT_FAILURE, @@ -1215,64 +1536,43 @@ inbound_sa_check(struct sa_ctx *sa_ctx, struct rte_mbuf *m, uint32_t sa_idx) return 0; } -static inline void -single_inbound_lookup(struct ipsec_sa *sadb, struct rte_mbuf *pkt, - struct ipsec_sa **sa_ret) +void +inbound_sa_lookup(struct sa_ctx *sa_ctx, struct rte_mbuf *pkts[], + void *sa_arr[], uint16_t nb_pkts) { - struct esp_hdr *esp; - struct ip *ip; - uint32_t *src4_addr; - uint8_t *src6_addr; + uint32_t i; + void *result_sa; struct ipsec_sa *sa; - *sa_ret = NULL; - - ip = rte_pktmbuf_mtod(pkt, struct ip *); - if (ip->ip_v == IPVERSION) - esp = (struct esp_hdr *)(ip + 1); - else - esp = (struct esp_hdr *)(((struct ip6_hdr *)ip) + 1); - - if (esp->spi == INVALID_SPI) - return; + sad_lookup(&sa_ctx->sad, pkts, sa_arr, nb_pkts); - sa = &sadb[SPI2IDX(rte_be_to_cpu_32(esp->spi))]; - if (rte_be_to_cpu_32(esp->spi) != sa->spi) - return; + /* + * Mark need for inline offload fallback on the LSB of SA pointer. + * Thanks to packet grouping mechanism which ipsec_process is using + * packets marked for fallback processing will form separate group. + * + * Because it is not safe to use SA pointer it is casted to generic + * pointer to prevent from unintentional use. Use ipsec_mask_saptr + * to get valid struct pointer. + */ + for (i = 0; i < nb_pkts; i++) { + if (sa_arr[i] == NULL) + continue; - switch (sa->flags) { - case IP4_TUNNEL: - src4_addr = RTE_PTR_ADD(ip, offsetof(struct ip, ip_src)); - if ((ip->ip_v == IPVERSION) && - (sa->src.ip.ip4 == *src4_addr) && - (sa->dst.ip.ip4 == *(src4_addr + 1))) - *sa_ret = sa; - break; - case IP6_TUNNEL: - src6_addr = RTE_PTR_ADD(ip, offsetof(struct ip6_hdr, ip6_src)); - if ((ip->ip_v == IP6_VERSION) && - !memcmp(&sa->src.ip.ip6.ip6, src6_addr, 16) && - !memcmp(&sa->dst.ip.ip6.ip6, src6_addr + 16, 16)) - *sa_ret = sa; - break; - case TRANSPORT: - *sa_ret = sa; + result_sa = sa = sa_arr[i]; + if (MBUF_NO_SEC_OFFLOAD(pkts[i]) && + sa->fallback_sessions > 0) { + uintptr_t intsa = (uintptr_t)sa; + intsa |= IPSEC_SA_OFFLOAD_FALLBACK_FLAG; + result_sa = (void *)intsa; + } + sa_arr[i] = result_sa; } } -void -inbound_sa_lookup(struct sa_ctx *sa_ctx, struct rte_mbuf *pkts[], - struct ipsec_sa *sa[], uint16_t nb_pkts) -{ - uint32_t i; - - for (i = 0; i < nb_pkts; i++) - single_inbound_lookup(sa_ctx->sa, pkts[i], &sa[i]); -} - void outbound_sa_lookup(struct sa_ctx *sa_ctx, uint32_t sa_idx[], - struct ipsec_sa *sa[], uint16_t nb_pkts) + void *sa[], uint16_t nb_pkts) { uint32_t i; @@ -1289,6 +1589,7 @@ sa_check_offloads(uint16_t port_id, uint64_t *rx_offloads, { struct ipsec_sa *rule; uint32_t idx_sa; + enum rte_security_session_action_type rule_type; *rx_offloads = 0; *tx_offloads = 0; @@ -1296,8 +1597,9 @@ sa_check_offloads(uint16_t port_id, uint64_t *rx_offloads, /* Check for inbound rules that use offloads and use this port */ for (idx_sa = 0; idx_sa < nb_sa_in; idx_sa++) { rule = &sa_in[idx_sa]; - if ((rule->type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO || - rule->type == + rule_type = ipsec_get_action_type(rule); + if ((rule_type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO || + rule_type == RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL) && rule->portid == port_id) *rx_offloads |= DEV_RX_OFFLOAD_SECURITY; @@ -1306,11 +1608,19 @@ sa_check_offloads(uint16_t port_id, uint64_t *rx_offloads, /* Check for outbound rules that use offloads and use this port */ for (idx_sa = 0; idx_sa < nb_sa_out; idx_sa++) { rule = &sa_out[idx_sa]; - if ((rule->type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO || - rule->type == + rule_type = ipsec_get_action_type(rule); + if ((rule_type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO || + rule_type == RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL) && rule->portid == port_id) *tx_offloads |= DEV_TX_OFFLOAD_SECURITY; } return 0; } + +void +sa_sort_arr(void) +{ + qsort(sa_in, nb_sa_in, sizeof(struct ipsec_sa), sa_cmp); + qsort(sa_out, nb_sa_out, sizeof(struct ipsec_sa), sa_cmp); +}