examples/ipsec-secgw: support flow director
[dpdk.git] / examples / ipsec-secgw / sa.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2016-2020 Intel Corporation
3  */
4
5 /*
6  * Security Associations
7  */
8 #include <sys/types.h>
9 #include <netinet/in.h>
10 #include <netinet/ip.h>
11 #include <netinet/ip6.h>
12
13 #include <rte_memzone.h>
14 #include <rte_crypto.h>
15 #include <rte_security.h>
16 #include <rte_cryptodev.h>
17 #include <rte_byteorder.h>
18 #include <rte_errno.h>
19 #include <rte_ip.h>
20 #include <rte_random.h>
21 #include <rte_ethdev.h>
22 #include <rte_malloc.h>
23
24 #include "ipsec.h"
25 #include "esp.h"
26 #include "parser.h"
27 #include "sad.h"
28
29 #define IPDEFTTL 64
30
31 #define IP4_FULL_MASK (sizeof(((struct ip_addr *)NULL)->ip.ip4) * CHAR_BIT)
32
33 #define IP6_FULL_MASK (sizeof(((struct ip_addr *)NULL)->ip.ip6.ip6) * CHAR_BIT)
34
35 #define MBUF_NO_SEC_OFFLOAD(m) ((m->ol_flags & PKT_RX_SEC_OFFLOAD) == 0)
36
37 struct supported_cipher_algo {
38         const char *keyword;
39         enum rte_crypto_cipher_algorithm algo;
40         uint16_t iv_len;
41         uint16_t block_size;
42         uint16_t key_len;
43 };
44
45 struct supported_auth_algo {
46         const char *keyword;
47         enum rte_crypto_auth_algorithm algo;
48         uint16_t digest_len;
49         uint16_t key_len;
50         uint8_t key_not_req;
51 };
52
53 struct supported_aead_algo {
54         const char *keyword;
55         enum rte_crypto_aead_algorithm algo;
56         uint16_t iv_len;
57         uint16_t block_size;
58         uint16_t digest_len;
59         uint16_t key_len;
60         uint8_t aad_len;
61 };
62
63
64 const struct supported_cipher_algo cipher_algos[] = {
65         {
66                 .keyword = "null",
67                 .algo = RTE_CRYPTO_CIPHER_NULL,
68                 .iv_len = 0,
69                 .block_size = 4,
70                 .key_len = 0
71         },
72         {
73                 .keyword = "aes-128-cbc",
74                 .algo = RTE_CRYPTO_CIPHER_AES_CBC,
75                 .iv_len = 16,
76                 .block_size = 16,
77                 .key_len = 16
78         },
79         {
80                 .keyword = "aes-192-cbc",
81                 .algo = RTE_CRYPTO_CIPHER_AES_CBC,
82                 .iv_len = 16,
83                 .block_size = 16,
84                 .key_len = 24
85         },
86         {
87                 .keyword = "aes-256-cbc",
88                 .algo = RTE_CRYPTO_CIPHER_AES_CBC,
89                 .iv_len = 16,
90                 .block_size = 16,
91                 .key_len = 32
92         },
93         {
94                 .keyword = "aes-128-ctr",
95                 .algo = RTE_CRYPTO_CIPHER_AES_CTR,
96                 .iv_len = 8,
97                 .block_size = 4,
98                 .key_len = 20
99         },
100         {
101                 .keyword = "3des-cbc",
102                 .algo = RTE_CRYPTO_CIPHER_3DES_CBC,
103                 .iv_len = 8,
104                 .block_size = 8,
105                 .key_len = 24
106         }
107 };
108
109 const struct supported_auth_algo auth_algos[] = {
110         {
111                 .keyword = "null",
112                 .algo = RTE_CRYPTO_AUTH_NULL,
113                 .digest_len = 0,
114                 .key_len = 0,
115                 .key_not_req = 1
116         },
117         {
118                 .keyword = "sha1-hmac",
119                 .algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
120                 .digest_len = 12,
121                 .key_len = 20
122         },
123         {
124                 .keyword = "sha256-hmac",
125                 .algo = RTE_CRYPTO_AUTH_SHA256_HMAC,
126                 .digest_len = 16,
127                 .key_len = 32
128         }
129 };
130
131 const struct supported_aead_algo aead_algos[] = {
132         {
133                 .keyword = "aes-128-gcm",
134                 .algo = RTE_CRYPTO_AEAD_AES_GCM,
135                 .iv_len = 8,
136                 .block_size = 4,
137                 .key_len = 20,
138                 .digest_len = 16,
139                 .aad_len = 8,
140         },
141         {
142                 .keyword = "aes-192-gcm",
143                 .algo = RTE_CRYPTO_AEAD_AES_GCM,
144                 .iv_len = 8,
145                 .block_size = 4,
146                 .key_len = 28,
147                 .digest_len = 16,
148                 .aad_len = 8,
149         },
150         {
151                 .keyword = "aes-256-gcm",
152                 .algo = RTE_CRYPTO_AEAD_AES_GCM,
153                 .iv_len = 8,
154                 .block_size = 4,
155                 .key_len = 36,
156                 .digest_len = 16,
157                 .aad_len = 8,
158         }
159 };
160
161 #define SA_INIT_NB      128
162
163 struct ipsec_sa *sa_out;
164 uint32_t nb_sa_out;
165 static uint32_t sa_out_sz;
166 static struct ipsec_sa_cnt sa_out_cnt;
167
168 struct ipsec_sa *sa_in;
169 uint32_t nb_sa_in;
170 static uint32_t sa_in_sz;
171 static struct ipsec_sa_cnt sa_in_cnt;
172
173 static const struct supported_cipher_algo *
174 find_match_cipher_algo(const char *cipher_keyword)
175 {
176         size_t i;
177
178         for (i = 0; i < RTE_DIM(cipher_algos); i++) {
179                 const struct supported_cipher_algo *algo =
180                         &cipher_algos[i];
181
182                 if (strcmp(cipher_keyword, algo->keyword) == 0)
183                         return algo;
184         }
185
186         return NULL;
187 }
188
189 static const struct supported_auth_algo *
190 find_match_auth_algo(const char *auth_keyword)
191 {
192         size_t i;
193
194         for (i = 0; i < RTE_DIM(auth_algos); i++) {
195                 const struct supported_auth_algo *algo =
196                         &auth_algos[i];
197
198                 if (strcmp(auth_keyword, algo->keyword) == 0)
199                         return algo;
200         }
201
202         return NULL;
203 }
204
205 static const struct supported_aead_algo *
206 find_match_aead_algo(const char *aead_keyword)
207 {
208         size_t i;
209
210         for (i = 0; i < RTE_DIM(aead_algos); i++) {
211                 const struct supported_aead_algo *algo =
212                         &aead_algos[i];
213
214                 if (strcmp(aead_keyword, algo->keyword) == 0)
215                         return algo;
216         }
217
218         return NULL;
219 }
220
221 /** parse_key_string
222  *  parse x:x:x:x.... hex number key string into uint8_t *key
223  *  return:
224  *  > 0: number of bytes parsed
225  *  0:   failed
226  */
227 static uint32_t
228 parse_key_string(const char *key_str, uint8_t *key)
229 {
230         const char *pt_start = key_str, *pt_end = key_str;
231         uint32_t nb_bytes = 0;
232
233         while (pt_end != NULL) {
234                 char sub_str[3] = {0};
235
236                 pt_end = strchr(pt_start, ':');
237
238                 if (pt_end == NULL) {
239                         if (strlen(pt_start) > 2)
240                                 return 0;
241                         strncpy(sub_str, pt_start, 2);
242                 } else {
243                         if (pt_end - pt_start > 2)
244                                 return 0;
245
246                         strncpy(sub_str, pt_start, pt_end - pt_start);
247                         pt_start = pt_end + 1;
248                 }
249
250                 key[nb_bytes++] = strtol(sub_str, NULL, 16);
251         }
252
253         return nb_bytes;
254 }
255
256 static int
257 extend_sa_arr(struct ipsec_sa **sa_tbl, uint32_t cur_cnt, uint32_t *cur_sz)
258 {
259         if (*sa_tbl == NULL) {
260                 *sa_tbl = calloc(SA_INIT_NB, sizeof(struct ipsec_sa));
261                 if (*sa_tbl == NULL)
262                         return -1;
263                 *cur_sz = SA_INIT_NB;
264                 return 0;
265         }
266
267         if (cur_cnt >= *cur_sz) {
268                 *sa_tbl = realloc(*sa_tbl,
269                         *cur_sz * sizeof(struct ipsec_sa) * 2);
270                 if (*sa_tbl == NULL)
271                         return -1;
272                 /* clean reallocated extra space */
273                 memset(&(*sa_tbl)[*cur_sz], 0,
274                         *cur_sz * sizeof(struct ipsec_sa));
275                 *cur_sz *= 2;
276         }
277
278         return 0;
279 }
280
281 void
282 parse_sa_tokens(char **tokens, uint32_t n_tokens,
283         struct parse_status *status)
284 {
285         struct ipsec_sa *rule = NULL;
286         struct rte_ipsec_session *ips;
287         uint32_t ti; /*token index*/
288         uint32_t *ri /*rule index*/;
289         struct ipsec_sa_cnt *sa_cnt;
290         uint32_t cipher_algo_p = 0;
291         uint32_t auth_algo_p = 0;
292         uint32_t aead_algo_p = 0;
293         uint32_t src_p = 0;
294         uint32_t dst_p = 0;
295         uint32_t mode_p = 0;
296         uint32_t type_p = 0;
297         uint32_t portid_p = 0;
298         uint32_t fallback_p = 0;
299         int16_t status_p = 0;
300
301         if (strcmp(tokens[0], "in") == 0) {
302                 ri = &nb_sa_in;
303                 sa_cnt = &sa_in_cnt;
304                 if (extend_sa_arr(&sa_in, nb_sa_in, &sa_in_sz) < 0)
305                         return;
306                 rule = &sa_in[*ri];
307                 rule->direction = RTE_SECURITY_IPSEC_SA_DIR_INGRESS;
308         } else {
309                 ri = &nb_sa_out;
310                 sa_cnt = &sa_out_cnt;
311                 if (extend_sa_arr(&sa_out, nb_sa_out, &sa_out_sz) < 0)
312                         return;
313                 rule = &sa_out[*ri];
314                 rule->direction = RTE_SECURITY_IPSEC_SA_DIR_EGRESS;
315         }
316
317         /* spi number */
318         APP_CHECK_TOKEN_IS_NUM(tokens, 1, status);
319         if (status->status < 0)
320                 return;
321         if (atoi(tokens[1]) == INVALID_SPI)
322                 return;
323         rule->spi = atoi(tokens[1]);
324         rule->portid = UINT16_MAX;
325         ips = ipsec_get_primary_session(rule);
326
327         for (ti = 2; ti < n_tokens; ti++) {
328                 if (strcmp(tokens[ti], "mode") == 0) {
329                         APP_CHECK_PRESENCE(mode_p, tokens[ti], status);
330                         if (status->status < 0)
331                                 return;
332
333                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
334                         if (status->status < 0)
335                                 return;
336
337                         if (strcmp(tokens[ti], "ipv4-tunnel") == 0) {
338                                 sa_cnt->nb_v4++;
339                                 rule->flags = IP4_TUNNEL;
340                         } else if (strcmp(tokens[ti], "ipv6-tunnel") == 0) {
341                                 sa_cnt->nb_v6++;
342                                 rule->flags = IP6_TUNNEL;
343                         } else if (strcmp(tokens[ti], "transport") == 0) {
344                                 sa_cnt->nb_v4++;
345                                 sa_cnt->nb_v6++;
346                                 rule->flags = TRANSPORT;
347                         } else {
348                                 APP_CHECK(0, status, "unrecognized "
349                                         "input \"%s\"", tokens[ti]);
350                                 return;
351                         }
352
353                         mode_p = 1;
354                         continue;
355                 }
356
357                 if (strcmp(tokens[ti], "cipher_algo") == 0) {
358                         const struct supported_cipher_algo *algo;
359                         uint32_t key_len;
360
361                         APP_CHECK_PRESENCE(cipher_algo_p, tokens[ti],
362                                 status);
363                         if (status->status < 0)
364                                 return;
365
366                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
367                         if (status->status < 0)
368                                 return;
369
370                         algo = find_match_cipher_algo(tokens[ti]);
371
372                         APP_CHECK(algo != NULL, status, "unrecognized "
373                                 "input \"%s\"", tokens[ti]);
374
375                         if (status->status < 0)
376                                 return;
377
378                         rule->cipher_algo = algo->algo;
379                         rule->block_size = algo->block_size;
380                         rule->iv_len = algo->iv_len;
381                         rule->cipher_key_len = algo->key_len;
382
383                         /* for NULL algorithm, no cipher key required */
384                         if (rule->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
385                                 cipher_algo_p = 1;
386                                 continue;
387                         }
388
389                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
390                         if (status->status < 0)
391                                 return;
392
393                         APP_CHECK(strcmp(tokens[ti], "cipher_key") == 0,
394                                 status, "unrecognized input \"%s\", "
395                                 "expect \"cipher_key\"", tokens[ti]);
396                         if (status->status < 0)
397                                 return;
398
399                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
400                         if (status->status < 0)
401                                 return;
402
403                         key_len = parse_key_string(tokens[ti],
404                                 rule->cipher_key);
405                         APP_CHECK(key_len == rule->cipher_key_len, status,
406                                 "unrecognized input \"%s\"", tokens[ti]);
407                         if (status->status < 0)
408                                 return;
409
410                         if (algo->algo == RTE_CRYPTO_CIPHER_AES_CBC ||
411                                 algo->algo == RTE_CRYPTO_CIPHER_3DES_CBC)
412                                 rule->salt = (uint32_t)rte_rand();
413
414                         if (algo->algo == RTE_CRYPTO_CIPHER_AES_CTR) {
415                                 key_len -= 4;
416                                 rule->cipher_key_len = key_len;
417                                 memcpy(&rule->salt,
418                                         &rule->cipher_key[key_len], 4);
419                         }
420
421                         cipher_algo_p = 1;
422                         continue;
423                 }
424
425                 if (strcmp(tokens[ti], "auth_algo") == 0) {
426                         const struct supported_auth_algo *algo;
427                         uint32_t key_len;
428
429                         APP_CHECK_PRESENCE(auth_algo_p, tokens[ti],
430                                 status);
431                         if (status->status < 0)
432                                 return;
433
434                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
435                         if (status->status < 0)
436                                 return;
437
438                         algo = find_match_auth_algo(tokens[ti]);
439                         APP_CHECK(algo != NULL, status, "unrecognized "
440                                 "input \"%s\"", tokens[ti]);
441
442                         if (status->status < 0)
443                                 return;
444
445                         rule->auth_algo = algo->algo;
446                         rule->auth_key_len = algo->key_len;
447                         rule->digest_len = algo->digest_len;
448
449                         /* NULL algorithm and combined algos do not
450                          * require auth key
451                          */
452                         if (algo->key_not_req) {
453                                 auth_algo_p = 1;
454                                 continue;
455                         }
456
457                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
458                         if (status->status < 0)
459                                 return;
460
461                         APP_CHECK(strcmp(tokens[ti], "auth_key") == 0,
462                                 status, "unrecognized input \"%s\", "
463                                 "expect \"auth_key\"", tokens[ti]);
464                         if (status->status < 0)
465                                 return;
466
467                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
468                         if (status->status < 0)
469                                 return;
470
471                         key_len = parse_key_string(tokens[ti],
472                                 rule->auth_key);
473                         APP_CHECK(key_len == rule->auth_key_len, status,
474                                 "unrecognized input \"%s\"", tokens[ti]);
475                         if (status->status < 0)
476                                 return;
477
478                         auth_algo_p = 1;
479                         continue;
480                 }
481
482                 if (strcmp(tokens[ti], "aead_algo") == 0) {
483                         const struct supported_aead_algo *algo;
484                         uint32_t key_len;
485
486                         APP_CHECK_PRESENCE(aead_algo_p, tokens[ti],
487                                 status);
488                         if (status->status < 0)
489                                 return;
490
491                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
492                         if (status->status < 0)
493                                 return;
494
495                         algo = find_match_aead_algo(tokens[ti]);
496
497                         APP_CHECK(algo != NULL, status, "unrecognized "
498                                 "input \"%s\"", tokens[ti]);
499
500                         if (status->status < 0)
501                                 return;
502
503                         rule->aead_algo = algo->algo;
504                         rule->cipher_key_len = algo->key_len;
505                         rule->digest_len = algo->digest_len;
506                         rule->aad_len = algo->aad_len;
507                         rule->block_size = algo->block_size;
508                         rule->iv_len = algo->iv_len;
509
510                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
511                         if (status->status < 0)
512                                 return;
513
514                         APP_CHECK(strcmp(tokens[ti], "aead_key") == 0,
515                                 status, "unrecognized input \"%s\", "
516                                 "expect \"aead_key\"", tokens[ti]);
517                         if (status->status < 0)
518                                 return;
519
520                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
521                         if (status->status < 0)
522                                 return;
523
524                         key_len = parse_key_string(tokens[ti],
525                                 rule->cipher_key);
526                         APP_CHECK(key_len == rule->cipher_key_len, status,
527                                 "unrecognized input \"%s\"", tokens[ti]);
528                         if (status->status < 0)
529                                 return;
530
531                         key_len -= 4;
532                         rule->cipher_key_len = key_len;
533                         memcpy(&rule->salt,
534                                 &rule->cipher_key[key_len], 4);
535
536                         aead_algo_p = 1;
537                         continue;
538                 }
539
540                 if (strcmp(tokens[ti], "src") == 0) {
541                         APP_CHECK_PRESENCE(src_p, tokens[ti], status);
542                         if (status->status < 0)
543                                 return;
544
545                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
546                         if (status->status < 0)
547                                 return;
548
549                         if (IS_IP4_TUNNEL(rule->flags)) {
550                                 struct in_addr ip;
551
552                                 APP_CHECK(parse_ipv4_addr(tokens[ti],
553                                         &ip, NULL) == 0, status,
554                                         "unrecognized input \"%s\", "
555                                         "expect valid ipv4 addr",
556                                         tokens[ti]);
557                                 if (status->status < 0)
558                                         return;
559                                 rule->src.ip.ip4 = rte_bswap32(
560                                         (uint32_t)ip.s_addr);
561                         } else if (IS_IP6_TUNNEL(rule->flags)) {
562                                 struct in6_addr ip;
563
564                                 APP_CHECK(parse_ipv6_addr(tokens[ti], &ip,
565                                         NULL) == 0, status,
566                                         "unrecognized input \"%s\", "
567                                         "expect valid ipv6 addr",
568                                         tokens[ti]);
569                                 if (status->status < 0)
570                                         return;
571                                 memcpy(rule->src.ip.ip6.ip6_b,
572                                         ip.s6_addr, 16);
573                         } else if (IS_TRANSPORT(rule->flags)) {
574                                 APP_CHECK(0, status, "unrecognized input "
575                                         "\"%s\"", tokens[ti]);
576                                 return;
577                         }
578
579                         src_p = 1;
580                         continue;
581                 }
582
583                 if (strcmp(tokens[ti], "dst") == 0) {
584                         APP_CHECK_PRESENCE(dst_p, tokens[ti], status);
585                         if (status->status < 0)
586                                 return;
587
588                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
589                         if (status->status < 0)
590                                 return;
591
592                         if (IS_IP4_TUNNEL(rule->flags)) {
593                                 struct in_addr ip;
594
595                                 APP_CHECK(parse_ipv4_addr(tokens[ti],
596                                         &ip, NULL) == 0, status,
597                                         "unrecognized input \"%s\", "
598                                         "expect valid ipv4 addr",
599                                         tokens[ti]);
600                                 if (status->status < 0)
601                                         return;
602                                 rule->dst.ip.ip4 = rte_bswap32(
603                                         (uint32_t)ip.s_addr);
604                         } else if (IS_IP6_TUNNEL(rule->flags)) {
605                                 struct in6_addr ip;
606
607                                 APP_CHECK(parse_ipv6_addr(tokens[ti], &ip,
608                                         NULL) == 0, status,
609                                         "unrecognized input \"%s\", "
610                                         "expect valid ipv6 addr",
611                                         tokens[ti]);
612                                 if (status->status < 0)
613                                         return;
614                                 memcpy(rule->dst.ip.ip6.ip6_b, ip.s6_addr, 16);
615                         } else if (IS_TRANSPORT(rule->flags)) {
616                                 APP_CHECK(0, status, "unrecognized "
617                                         "input \"%s\"", tokens[ti]);
618                                 return;
619                         }
620
621                         dst_p = 1;
622                         continue;
623                 }
624
625                 if (strcmp(tokens[ti], "type") == 0) {
626                         APP_CHECK_PRESENCE(type_p, tokens[ti], status);
627                         if (status->status < 0)
628                                 return;
629
630                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
631                         if (status->status < 0)
632                                 return;
633
634                         if (strcmp(tokens[ti], "inline-crypto-offload") == 0)
635                                 ips->type =
636                                         RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO;
637                         else if (strcmp(tokens[ti],
638                                         "inline-protocol-offload") == 0)
639                                 ips->type =
640                                 RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL;
641                         else if (strcmp(tokens[ti],
642                                         "lookaside-protocol-offload") == 0)
643                                 ips->type =
644                                 RTE_SECURITY_ACTION_TYPE_LOOKASIDE_PROTOCOL;
645                         else if (strcmp(tokens[ti], "no-offload") == 0)
646                                 ips->type = RTE_SECURITY_ACTION_TYPE_NONE;
647                         else if (strcmp(tokens[ti], "cpu-crypto") == 0)
648                                 ips->type = RTE_SECURITY_ACTION_TYPE_CPU_CRYPTO;
649                         else {
650                                 APP_CHECK(0, status, "Invalid input \"%s\"",
651                                                 tokens[ti]);
652                                 return;
653                         }
654
655                         type_p = 1;
656                         continue;
657                 }
658
659                 if (strcmp(tokens[ti], "port_id") == 0) {
660                         APP_CHECK_PRESENCE(portid_p, tokens[ti], status);
661                         if (status->status < 0)
662                                 return;
663                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
664                         if (status->status < 0)
665                                 return;
666                         if (rule->portid == UINT16_MAX)
667                                 rule->portid = atoi(tokens[ti]);
668                         else if (rule->portid != atoi(tokens[ti])) {
669                                 APP_CHECK(0, status,
670                                         "portid %s not matching with already assigned portid %u",
671                                         tokens[ti], rule->portid);
672                                 return;
673                         }
674                         portid_p = 1;
675                         continue;
676                 }
677
678                 if (strcmp(tokens[ti], "fallback") == 0) {
679                         struct rte_ipsec_session *fb;
680
681                         APP_CHECK(app_sa_prm.enable, status, "Fallback session "
682                                 "not allowed for legacy mode.");
683                         if (status->status < 0)
684                                 return;
685                         APP_CHECK(ips->type ==
686                                 RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO, status,
687                                 "Fallback session allowed if primary session "
688                                 "is of type inline-crypto-offload only.");
689                         if (status->status < 0)
690                                 return;
691                         APP_CHECK(rule->direction ==
692                                 RTE_SECURITY_IPSEC_SA_DIR_INGRESS, status,
693                                 "Fallback session not allowed for egress "
694                                 "rule");
695                         if (status->status < 0)
696                                 return;
697                         APP_CHECK_PRESENCE(fallback_p, tokens[ti], status);
698                         if (status->status < 0)
699                                 return;
700                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
701                         if (status->status < 0)
702                                 return;
703                         fb = ipsec_get_fallback_session(rule);
704                         if (strcmp(tokens[ti], "lookaside-none") == 0)
705                                 fb->type = RTE_SECURITY_ACTION_TYPE_NONE;
706                         else if (strcmp(tokens[ti], "cpu-crypto") == 0)
707                                 fb->type = RTE_SECURITY_ACTION_TYPE_CPU_CRYPTO;
708                         else {
709                                 APP_CHECK(0, status, "unrecognized fallback "
710                                         "type %s.", tokens[ti]);
711                                 return;
712                         }
713
714                         rule->fallback_sessions = 1;
715                         fallback_p = 1;
716                         continue;
717                 }
718                 if (strcmp(tokens[ti], "flow-direction") == 0) {
719                         switch (ips->type) {
720                         case RTE_SECURITY_ACTION_TYPE_NONE:
721                         case RTE_SECURITY_ACTION_TYPE_CPU_CRYPTO:
722                                 rule->fdir_flag = 1;
723                                 INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
724                                 if (status->status < 0)
725                                         return;
726                                 if (rule->portid == UINT16_MAX)
727                                         rule->portid = atoi(tokens[ti]);
728                                 else if (rule->portid != atoi(tokens[ti])) {
729                                         APP_CHECK(0, status,
730                                                 "portid %s not matching with already assigned portid %u",
731                                                 tokens[ti], rule->portid);
732                                         return;
733                                 }
734                                 INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
735                                 if (status->status < 0)
736                                         return;
737                                 rule->fdir_qid = atoi(tokens[ti]);
738                                 /* validating portid and queueid */
739                                 status_p = check_flow_params(rule->portid,
740                                                 rule->fdir_qid);
741                                 if (status_p < 0) {
742                                         printf("port id %u / queue id %u is "
743                                                 "not valid\n", rule->portid,
744                                                  rule->fdir_qid);
745                                 }
746                                 break;
747                         case RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO:
748                         case RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL:
749                         case RTE_SECURITY_ACTION_TYPE_LOOKASIDE_PROTOCOL:
750                         default:
751                                 APP_CHECK(0, status,
752                                         "flow director not supported for security session type %d",
753                                         ips->type);
754                                 return;
755                         }
756                         continue;
757                 }
758
759                 /* unrecognizeable input */
760                 APP_CHECK(0, status, "unrecognized input \"%s\"",
761                         tokens[ti]);
762                 return;
763         }
764
765         if (aead_algo_p) {
766                 APP_CHECK(cipher_algo_p == 0, status,
767                                 "AEAD used, no need for cipher options");
768                 if (status->status < 0)
769                         return;
770
771                 APP_CHECK(auth_algo_p == 0, status,
772                                 "AEAD used, no need for auth options");
773                 if (status->status < 0)
774                         return;
775         } else {
776                 APP_CHECK(cipher_algo_p == 1, status, "missing cipher or AEAD options");
777                 if (status->status < 0)
778                         return;
779
780                 APP_CHECK(auth_algo_p == 1, status, "missing auth or AEAD options");
781                 if (status->status < 0)
782                         return;
783         }
784
785         APP_CHECK(mode_p == 1, status, "missing mode option");
786         if (status->status < 0)
787                 return;
788
789         if ((ips->type != RTE_SECURITY_ACTION_TYPE_NONE && ips->type !=
790                         RTE_SECURITY_ACTION_TYPE_CPU_CRYPTO) && (portid_p == 0))
791                 printf("Missing portid option, falling back to non-offload\n");
792
793         if (!type_p || (!portid_p && ips->type !=
794                         RTE_SECURITY_ACTION_TYPE_CPU_CRYPTO)) {
795                 ips->type = RTE_SECURITY_ACTION_TYPE_NONE;
796         }
797
798         *ri = *ri + 1;
799 }
800
801 static void
802 print_one_sa_rule(const struct ipsec_sa *sa, int inbound)
803 {
804         uint32_t i;
805         uint8_t a, b, c, d;
806         const struct rte_ipsec_session *ips;
807         const struct rte_ipsec_session *fallback_ips;
808
809         printf("\tspi_%s(%3u):", inbound?"in":"out", sa->spi);
810
811         for (i = 0; i < RTE_DIM(cipher_algos); i++) {
812                 if (cipher_algos[i].algo == sa->cipher_algo &&
813                                 cipher_algos[i].key_len == sa->cipher_key_len) {
814                         printf("%s ", cipher_algos[i].keyword);
815                         break;
816                 }
817         }
818
819         for (i = 0; i < RTE_DIM(auth_algos); i++) {
820                 if (auth_algos[i].algo == sa->auth_algo) {
821                         printf("%s ", auth_algos[i].keyword);
822                         break;
823                 }
824         }
825
826         for (i = 0; i < RTE_DIM(aead_algos); i++) {
827                 if (aead_algos[i].algo == sa->aead_algo &&
828                                 aead_algos[i].key_len-4 == sa->cipher_key_len) {
829                         printf("%s ", aead_algos[i].keyword);
830                         break;
831                 }
832         }
833
834         printf("mode:");
835
836         switch (WITHOUT_TRANSPORT_VERSION(sa->flags)) {
837         case IP4_TUNNEL:
838                 printf("IP4Tunnel ");
839                 uint32_t_to_char(sa->src.ip.ip4, &a, &b, &c, &d);
840                 printf("%hhu.%hhu.%hhu.%hhu ", d, c, b, a);
841                 uint32_t_to_char(sa->dst.ip.ip4, &a, &b, &c, &d);
842                 printf("%hhu.%hhu.%hhu.%hhu", d, c, b, a);
843                 break;
844         case IP6_TUNNEL:
845                 printf("IP6Tunnel ");
846                 for (i = 0; i < 16; i++) {
847                         if (i % 2 && i != 15)
848                                 printf("%.2x:", sa->src.ip.ip6.ip6_b[i]);
849                         else
850                                 printf("%.2x", sa->src.ip.ip6.ip6_b[i]);
851                 }
852                 printf(" ");
853                 for (i = 0; i < 16; i++) {
854                         if (i % 2 && i != 15)
855                                 printf("%.2x:", sa->dst.ip.ip6.ip6_b[i]);
856                         else
857                                 printf("%.2x", sa->dst.ip.ip6.ip6_b[i]);
858                 }
859                 break;
860         case TRANSPORT:
861                 printf("Transport ");
862                 break;
863         }
864
865         ips = &sa->sessions[IPSEC_SESSION_PRIMARY];
866         printf(" type:");
867         switch (ips->type) {
868         case RTE_SECURITY_ACTION_TYPE_NONE:
869                 printf("no-offload ");
870                 break;
871         case RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO:
872                 printf("inline-crypto-offload ");
873                 break;
874         case RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL:
875                 printf("inline-protocol-offload ");
876                 break;
877         case RTE_SECURITY_ACTION_TYPE_LOOKASIDE_PROTOCOL:
878                 printf("lookaside-protocol-offload ");
879                 break;
880         case RTE_SECURITY_ACTION_TYPE_CPU_CRYPTO:
881                 printf("cpu-crypto-accelerated ");
882                 break;
883         }
884
885         fallback_ips = &sa->sessions[IPSEC_SESSION_FALLBACK];
886         if (fallback_ips != NULL && sa->fallback_sessions > 0) {
887                 printf("inline fallback: ");
888                 switch (fallback_ips->type) {
889                 case RTE_SECURITY_ACTION_TYPE_NONE:
890                         printf("lookaside-none");
891                         break;
892                 case RTE_SECURITY_ACTION_TYPE_CPU_CRYPTO:
893                         printf("cpu-crypto-accelerated");
894                         break;
895                 default:
896                         printf("invalid");
897                         break;
898                 }
899         }
900         if (sa->fdir_flag == 1)
901                 printf("flow-direction port %d queue %d", sa->portid,
902                                 sa->fdir_qid);
903
904         printf("\n");
905 }
906
907 static struct sa_ctx *
908 sa_create(const char *name, int32_t socket_id, uint32_t nb_sa)
909 {
910         char s[PATH_MAX];
911         struct sa_ctx *sa_ctx;
912         uint32_t mz_size;
913         const struct rte_memzone *mz;
914
915         snprintf(s, sizeof(s), "%s_%u", name, socket_id);
916
917         /* Create SA context */
918         printf("Creating SA context with %u maximum entries on socket %d\n",
919                         nb_sa, socket_id);
920
921         mz_size = sizeof(struct ipsec_xf) * nb_sa;
922         mz = rte_memzone_reserve(s, mz_size, socket_id,
923                         RTE_MEMZONE_1GB | RTE_MEMZONE_SIZE_HINT_ONLY);
924         if (mz == NULL) {
925                 printf("Failed to allocate SA XFORM memory\n");
926                 rte_errno = ENOMEM;
927                 return NULL;
928         }
929
930         sa_ctx = rte_zmalloc(NULL, sizeof(struct sa_ctx) +
931                 sizeof(struct ipsec_sa) * nb_sa, RTE_CACHE_LINE_SIZE);
932
933         if (sa_ctx == NULL) {
934                 printf("Failed to allocate SA CTX memory\n");
935                 rte_errno = ENOMEM;
936                 rte_memzone_free(mz);
937                 return NULL;
938         }
939
940         sa_ctx->xf = (struct ipsec_xf *)mz->addr;
941         sa_ctx->nb_sa = nb_sa;
942
943         return sa_ctx;
944 }
945
946 static int
947 check_eth_dev_caps(uint16_t portid, uint32_t inbound)
948 {
949         struct rte_eth_dev_info dev_info;
950         int retval;
951
952         retval = rte_eth_dev_info_get(portid, &dev_info);
953         if (retval != 0) {
954                 RTE_LOG(ERR, IPSEC,
955                         "Error during getting device (port %u) info: %s\n",
956                         portid, strerror(-retval));
957
958                 return retval;
959         }
960
961         if (inbound) {
962                 if ((dev_info.rx_offload_capa &
963                                 DEV_RX_OFFLOAD_SECURITY) == 0) {
964                         RTE_LOG(WARNING, PORT,
965                                 "hardware RX IPSec offload is not supported\n");
966                         return -EINVAL;
967                 }
968
969         } else { /* outbound */
970                 if ((dev_info.tx_offload_capa &
971                                 DEV_TX_OFFLOAD_SECURITY) == 0) {
972                         RTE_LOG(WARNING, PORT,
973                                 "hardware TX IPSec offload is not supported\n");
974                         return -EINVAL;
975                 }
976         }
977         return 0;
978 }
979
980 /*
981  * Helper function, tries to determine next_proto for SPI
982  * by searching though SP rules.
983  */
984 static int
985 get_spi_proto(uint32_t spi, enum rte_security_ipsec_sa_direction dir,
986                 struct ip_addr ip_addr[2], uint32_t mask[2])
987 {
988         int32_t rc4, rc6;
989
990         rc4 = sp4_spi_present(spi, dir == RTE_SECURITY_IPSEC_SA_DIR_INGRESS,
991                                 ip_addr, mask);
992         rc6 = sp6_spi_present(spi, dir == RTE_SECURITY_IPSEC_SA_DIR_INGRESS,
993                                 ip_addr, mask);
994
995         if (rc4 >= 0) {
996                 if (rc6 >= 0) {
997                         RTE_LOG(ERR, IPSEC,
998                                 "%s: SPI %u used simultaeously by "
999                                 "IPv4(%d) and IPv6 (%d) SP rules\n",
1000                                 __func__, spi, rc4, rc6);
1001                         return -EINVAL;
1002                 } else
1003                         return IPPROTO_IPIP;
1004         } else if (rc6 < 0) {
1005                 RTE_LOG(ERR, IPSEC,
1006                         "%s: SPI %u is not used by any SP rule\n",
1007                         __func__, spi);
1008                 return -EINVAL;
1009         } else
1010                 return IPPROTO_IPV6;
1011 }
1012
1013 /*
1014  * Helper function for getting source and destination IP addresses
1015  * from SP. Needed for inline crypto transport mode, as addresses are not
1016  * provided in config file for that mode. It checks if SP for current SA exists,
1017  * and based on what type of protocol is returned, it stores appropriate
1018  * addresses got from SP into SA.
1019  */
1020 static int
1021 sa_add_address_inline_crypto(struct ipsec_sa *sa)
1022 {
1023         int protocol;
1024         struct ip_addr ip_addr[2];
1025         uint32_t mask[2];
1026
1027         protocol = get_spi_proto(sa->spi, sa->direction, ip_addr, mask);
1028         if (protocol < 0)
1029                 return protocol;
1030         else if (protocol == IPPROTO_IPIP) {
1031                 sa->flags |= IP4_TRANSPORT;
1032                 if (mask[0] == IP4_FULL_MASK &&
1033                                 mask[1] == IP4_FULL_MASK &&
1034                                 ip_addr[0].ip.ip4 != 0 &&
1035                                 ip_addr[1].ip.ip4 != 0) {
1036
1037                         sa->src.ip.ip4 = ip_addr[0].ip.ip4;
1038                         sa->dst.ip.ip4 = ip_addr[1].ip.ip4;
1039                 } else {
1040                         RTE_LOG(ERR, IPSEC,
1041                         "%s: No valid address or mask entry in"
1042                         " IPv4 SP rule for SPI %u\n",
1043                         __func__, sa->spi);
1044                         return -EINVAL;
1045                 }
1046         } else if (protocol == IPPROTO_IPV6) {
1047                 sa->flags |= IP6_TRANSPORT;
1048                 if (mask[0] == IP6_FULL_MASK &&
1049                                 mask[1] == IP6_FULL_MASK &&
1050                                 (ip_addr[0].ip.ip6.ip6[0] != 0 ||
1051                                 ip_addr[0].ip.ip6.ip6[1] != 0) &&
1052                                 (ip_addr[1].ip.ip6.ip6[0] != 0 ||
1053                                 ip_addr[1].ip.ip6.ip6[1] != 0)) {
1054
1055                         sa->src.ip.ip6 = ip_addr[0].ip.ip6;
1056                         sa->dst.ip.ip6 = ip_addr[1].ip.ip6;
1057                 } else {
1058                         RTE_LOG(ERR, IPSEC,
1059                         "%s: No valid address or mask entry in"
1060                         " IPv6 SP rule for SPI %u\n",
1061                         __func__, sa->spi);
1062                         return -EINVAL;
1063                 }
1064         }
1065         return 0;
1066 }
1067
1068 static int
1069 sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
1070                 uint32_t nb_entries, uint32_t inbound,
1071                 struct socket_ctx *skt_ctx)
1072 {
1073         struct ipsec_sa *sa;
1074         uint32_t i, idx;
1075         uint16_t iv_length, aad_length;
1076         int inline_status;
1077         int32_t rc;
1078         struct rte_ipsec_session *ips;
1079
1080         /* for ESN upper 32 bits of SQN also need to be part of AAD */
1081         aad_length = (app_sa_prm.enable_esn != 0) ? sizeof(uint32_t) : 0;
1082
1083         for (i = 0; i < nb_entries; i++) {
1084                 idx = i;
1085                 sa = &sa_ctx->sa[idx];
1086                 if (sa->spi != 0) {
1087                         printf("Index %u already in use by SPI %u\n",
1088                                         idx, sa->spi);
1089                         return -EINVAL;
1090                 }
1091                 *sa = entries[i];
1092
1093                 if (inbound) {
1094                         rc = ipsec_sad_add(&sa_ctx->sad, sa);
1095                         if (rc != 0)
1096                                 return rc;
1097                 }
1098
1099                 sa->seq = 0;
1100                 ips = ipsec_get_primary_session(sa);
1101
1102                 if (ips->type == RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL ||
1103                         ips->type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO) {
1104                         if (check_eth_dev_caps(sa->portid, inbound))
1105                                 return -EINVAL;
1106                 }
1107
1108                 switch (WITHOUT_TRANSPORT_VERSION(sa->flags)) {
1109                 case IP4_TUNNEL:
1110                         sa->src.ip.ip4 = rte_cpu_to_be_32(sa->src.ip.ip4);
1111                         sa->dst.ip.ip4 = rte_cpu_to_be_32(sa->dst.ip.ip4);
1112                         break;
1113                 case TRANSPORT:
1114                         if (ips->type ==
1115                                 RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO) {
1116                                 inline_status =
1117                                         sa_add_address_inline_crypto(sa);
1118                                 if (inline_status < 0)
1119                                         return inline_status;
1120                         }
1121                         break;
1122                 }
1123
1124                 if (sa->aead_algo == RTE_CRYPTO_AEAD_AES_GCM) {
1125                         iv_length = 12;
1126
1127                         sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_AEAD;
1128                         sa_ctx->xf[idx].a.aead.algo = sa->aead_algo;
1129                         sa_ctx->xf[idx].a.aead.key.data = sa->cipher_key;
1130                         sa_ctx->xf[idx].a.aead.key.length =
1131                                 sa->cipher_key_len;
1132                         sa_ctx->xf[idx].a.aead.op = (inbound == 1) ?
1133                                 RTE_CRYPTO_AEAD_OP_DECRYPT :
1134                                 RTE_CRYPTO_AEAD_OP_ENCRYPT;
1135                         sa_ctx->xf[idx].a.next = NULL;
1136                         sa_ctx->xf[idx].a.aead.iv.offset = IV_OFFSET;
1137                         sa_ctx->xf[idx].a.aead.iv.length = iv_length;
1138                         sa_ctx->xf[idx].a.aead.aad_length =
1139                                 sa->aad_len + aad_length;
1140                         sa_ctx->xf[idx].a.aead.digest_length =
1141                                 sa->digest_len;
1142
1143                         sa->xforms = &sa_ctx->xf[idx].a;
1144                 } else {
1145                         switch (sa->cipher_algo) {
1146                         case RTE_CRYPTO_CIPHER_NULL:
1147                         case RTE_CRYPTO_CIPHER_3DES_CBC:
1148                         case RTE_CRYPTO_CIPHER_AES_CBC:
1149                                 iv_length = sa->iv_len;
1150                                 break;
1151                         case RTE_CRYPTO_CIPHER_AES_CTR:
1152                                 iv_length = 16;
1153                                 break;
1154                         default:
1155                                 RTE_LOG(ERR, IPSEC_ESP,
1156                                                 "unsupported cipher algorithm %u\n",
1157                                                 sa->cipher_algo);
1158                                 return -EINVAL;
1159                         }
1160
1161                         if (inbound) {
1162                                 sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
1163                                 sa_ctx->xf[idx].b.cipher.algo = sa->cipher_algo;
1164                                 sa_ctx->xf[idx].b.cipher.key.data = sa->cipher_key;
1165                                 sa_ctx->xf[idx].b.cipher.key.length =
1166                                         sa->cipher_key_len;
1167                                 sa_ctx->xf[idx].b.cipher.op =
1168                                         RTE_CRYPTO_CIPHER_OP_DECRYPT;
1169                                 sa_ctx->xf[idx].b.next = NULL;
1170                                 sa_ctx->xf[idx].b.cipher.iv.offset = IV_OFFSET;
1171                                 sa_ctx->xf[idx].b.cipher.iv.length = iv_length;
1172
1173                                 sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_AUTH;
1174                                 sa_ctx->xf[idx].a.auth.algo = sa->auth_algo;
1175                                 sa_ctx->xf[idx].a.auth.key.data = sa->auth_key;
1176                                 sa_ctx->xf[idx].a.auth.key.length =
1177                                         sa->auth_key_len;
1178                                 sa_ctx->xf[idx].a.auth.digest_length =
1179                                         sa->digest_len;
1180                                 sa_ctx->xf[idx].a.auth.op =
1181                                         RTE_CRYPTO_AUTH_OP_VERIFY;
1182                         } else { /* outbound */
1183                                 sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
1184                                 sa_ctx->xf[idx].a.cipher.algo = sa->cipher_algo;
1185                                 sa_ctx->xf[idx].a.cipher.key.data = sa->cipher_key;
1186                                 sa_ctx->xf[idx].a.cipher.key.length =
1187                                         sa->cipher_key_len;
1188                                 sa_ctx->xf[idx].a.cipher.op =
1189                                         RTE_CRYPTO_CIPHER_OP_ENCRYPT;
1190                                 sa_ctx->xf[idx].a.next = NULL;
1191                                 sa_ctx->xf[idx].a.cipher.iv.offset = IV_OFFSET;
1192                                 sa_ctx->xf[idx].a.cipher.iv.length = iv_length;
1193
1194                                 sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_AUTH;
1195                                 sa_ctx->xf[idx].b.auth.algo = sa->auth_algo;
1196                                 sa_ctx->xf[idx].b.auth.key.data = sa->auth_key;
1197                                 sa_ctx->xf[idx].b.auth.key.length =
1198                                         sa->auth_key_len;
1199                                 sa_ctx->xf[idx].b.auth.digest_length =
1200                                         sa->digest_len;
1201                                 sa_ctx->xf[idx].b.auth.op =
1202                                         RTE_CRYPTO_AUTH_OP_GENERATE;
1203                         }
1204
1205                         sa_ctx->xf[idx].a.next = &sa_ctx->xf[idx].b;
1206                         sa_ctx->xf[idx].b.next = NULL;
1207                         sa->xforms = &sa_ctx->xf[idx].a;
1208                 }
1209
1210                 if (ips->type ==
1211                         RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL ||
1212                         ips->type ==
1213                         RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO) {
1214                         rc = create_inline_session(skt_ctx, sa, ips);
1215                         if (rc != 0) {
1216                                 RTE_LOG(ERR, IPSEC_ESP,
1217                                         "create_inline_session() failed\n");
1218                                 return -EINVAL;
1219                         }
1220                 }
1221
1222                 if (sa->fdir_flag && inbound) {
1223                         rc = create_ipsec_esp_flow(sa);
1224                         if (rc != 0)
1225                                 RTE_LOG(ERR, IPSEC_ESP,
1226                                         "create_ipsec_esp_flow() failed %s\n",
1227                                         strerror(rc));
1228                 }
1229                 print_one_sa_rule(sa, inbound);
1230         }
1231
1232         return 0;
1233 }
1234
1235 static inline int
1236 sa_out_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
1237                 uint32_t nb_entries, struct socket_ctx *skt_ctx)
1238 {
1239         return sa_add_rules(sa_ctx, entries, nb_entries, 0, skt_ctx);
1240 }
1241
1242 static inline int
1243 sa_in_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
1244                 uint32_t nb_entries, struct socket_ctx *skt_ctx)
1245 {
1246         return sa_add_rules(sa_ctx, entries, nb_entries, 1, skt_ctx);
1247 }
1248
1249 /*
1250  * helper function, fills parameters that are identical for all SAs
1251  */
1252 static void
1253 fill_ipsec_app_sa_prm(struct rte_ipsec_sa_prm *prm,
1254         const struct app_sa_prm *app_prm)
1255 {
1256         memset(prm, 0, sizeof(*prm));
1257
1258         prm->flags = app_prm->flags;
1259         prm->ipsec_xform.options.esn = app_prm->enable_esn;
1260         prm->ipsec_xform.replay_win_sz = app_prm->window_size;
1261 }
1262
1263 static int
1264 fill_ipsec_sa_prm(struct rte_ipsec_sa_prm *prm, const struct ipsec_sa *ss,
1265         const struct rte_ipv4_hdr *v4, struct rte_ipv6_hdr *v6)
1266 {
1267         int32_t rc;
1268
1269         /*
1270          * Try to get SPI next proto by searching that SPI in SPD.
1271          * probably not the optimal way, but there seems nothing
1272          * better right now.
1273          */
1274         rc = get_spi_proto(ss->spi, ss->direction, NULL, NULL);
1275         if (rc < 0)
1276                 return rc;
1277
1278         fill_ipsec_app_sa_prm(prm, &app_sa_prm);
1279         prm->userdata = (uintptr_t)ss;
1280
1281         /* setup ipsec xform */
1282         prm->ipsec_xform.spi = ss->spi;
1283         prm->ipsec_xform.salt = ss->salt;
1284         prm->ipsec_xform.direction = ss->direction;
1285         prm->ipsec_xform.proto = RTE_SECURITY_IPSEC_SA_PROTO_ESP;
1286         prm->ipsec_xform.mode = (IS_TRANSPORT(ss->flags)) ?
1287                 RTE_SECURITY_IPSEC_SA_MODE_TRANSPORT :
1288                 RTE_SECURITY_IPSEC_SA_MODE_TUNNEL;
1289         prm->ipsec_xform.options.ecn = 1;
1290         prm->ipsec_xform.options.copy_dscp = 1;
1291
1292         if (IS_IP4_TUNNEL(ss->flags)) {
1293                 prm->ipsec_xform.tunnel.type = RTE_SECURITY_IPSEC_TUNNEL_IPV4;
1294                 prm->tun.hdr_len = sizeof(*v4);
1295                 prm->tun.next_proto = rc;
1296                 prm->tun.hdr = v4;
1297         } else if (IS_IP6_TUNNEL(ss->flags)) {
1298                 prm->ipsec_xform.tunnel.type = RTE_SECURITY_IPSEC_TUNNEL_IPV6;
1299                 prm->tun.hdr_len = sizeof(*v6);
1300                 prm->tun.next_proto = rc;
1301                 prm->tun.hdr = v6;
1302         } else {
1303                 /* transport mode */
1304                 prm->trs.proto = rc;
1305         }
1306
1307         /* setup crypto section */
1308         prm->crypto_xform = ss->xforms;
1309         return 0;
1310 }
1311
1312 static int
1313 fill_ipsec_session(struct rte_ipsec_session *ss, struct rte_ipsec_sa *sa)
1314 {
1315         int32_t rc = 0;
1316
1317         ss->sa = sa;
1318
1319         if (ss->type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO ||
1320                 ss->type == RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL) {
1321                 if (ss->security.ses != NULL) {
1322                         rc = rte_ipsec_session_prepare(ss);
1323                         if (rc != 0)
1324                                 memset(ss, 0, sizeof(*ss));
1325                 }
1326         }
1327
1328         return rc;
1329 }
1330
1331 /*
1332  * Initialise related rte_ipsec_sa object.
1333  */
1334 static int
1335 ipsec_sa_init(struct ipsec_sa *lsa, struct rte_ipsec_sa *sa, uint32_t sa_size)
1336 {
1337         int rc;
1338         struct rte_ipsec_sa_prm prm;
1339         struct rte_ipsec_session *ips;
1340         struct rte_ipv4_hdr v4  = {
1341                 .version_ihl = IPVERSION << 4 |
1342                         sizeof(v4) / RTE_IPV4_IHL_MULTIPLIER,
1343                 .time_to_live = IPDEFTTL,
1344                 .next_proto_id = IPPROTO_ESP,
1345                 .src_addr = lsa->src.ip.ip4,
1346                 .dst_addr = lsa->dst.ip.ip4,
1347         };
1348         struct rte_ipv6_hdr v6 = {
1349                 .vtc_flow = htonl(IP6_VERSION << 28),
1350                 .proto = IPPROTO_ESP,
1351         };
1352
1353         if (IS_IP6_TUNNEL(lsa->flags)) {
1354                 memcpy(v6.src_addr, lsa->src.ip.ip6.ip6_b, sizeof(v6.src_addr));
1355                 memcpy(v6.dst_addr, lsa->dst.ip.ip6.ip6_b, sizeof(v6.dst_addr));
1356         }
1357
1358         rc = fill_ipsec_sa_prm(&prm, lsa, &v4, &v6);
1359         if (rc == 0)
1360                 rc = rte_ipsec_sa_init(sa, &prm, sa_size);
1361         if (rc < 0)
1362                 return rc;
1363
1364         /* init primary processing session */
1365         ips = ipsec_get_primary_session(lsa);
1366         rc = fill_ipsec_session(ips, sa);
1367         if (rc != 0)
1368                 return rc;
1369
1370         /* init inline fallback processing session */
1371         if (lsa->fallback_sessions == 1)
1372                 rc = fill_ipsec_session(ipsec_get_fallback_session(lsa), sa);
1373
1374         return rc;
1375 }
1376
1377 /*
1378  * Allocate space and init rte_ipsec_sa strcutures,
1379  * one per session.
1380  */
1381 static int
1382 ipsec_satbl_init(struct sa_ctx *ctx, uint32_t nb_ent, int32_t socket)
1383 {
1384         int32_t rc, sz;
1385         uint32_t i, idx;
1386         size_t tsz;
1387         struct rte_ipsec_sa *sa;
1388         struct ipsec_sa *lsa;
1389         struct rte_ipsec_sa_prm prm;
1390
1391         /* determine SA size */
1392         idx = 0;
1393         fill_ipsec_sa_prm(&prm, ctx->sa + idx, NULL, NULL);
1394         sz = rte_ipsec_sa_size(&prm);
1395         if (sz < 0) {
1396                 RTE_LOG(ERR, IPSEC, "%s(%p, %u, %d): "
1397                         "failed to determine SA size, error code: %d\n",
1398                         __func__, ctx, nb_ent, socket, sz);
1399                 return sz;
1400         }
1401
1402         tsz = sz * nb_ent;
1403
1404         ctx->satbl = rte_zmalloc_socket(NULL, tsz, RTE_CACHE_LINE_SIZE, socket);
1405         if (ctx->satbl == NULL) {
1406                 RTE_LOG(ERR, IPSEC,
1407                         "%s(%p, %u, %d): failed to allocate %zu bytes\n",
1408                         __func__,  ctx, nb_ent, socket, tsz);
1409                 return -ENOMEM;
1410         }
1411
1412         rc = 0;
1413         for (i = 0; i != nb_ent && rc == 0; i++) {
1414
1415                 idx = i;
1416
1417                 sa = (struct rte_ipsec_sa *)((uintptr_t)ctx->satbl + sz * i);
1418                 lsa = ctx->sa + idx;
1419
1420                 rc = ipsec_sa_init(lsa, sa, sz);
1421         }
1422
1423         return rc;
1424 }
1425
1426 static int
1427 sa_cmp(const void *p, const void *q)
1428 {
1429         uint32_t spi1 = ((const struct ipsec_sa *)p)->spi;
1430         uint32_t spi2 = ((const struct ipsec_sa *)q)->spi;
1431
1432         return (int)(spi1 - spi2);
1433 }
1434
1435 /*
1436  * Walk through all SA rules to find an SA with given SPI
1437  */
1438 int
1439 sa_spi_present(struct sa_ctx *sa_ctx, uint32_t spi, int inbound)
1440 {
1441         uint32_t num;
1442         struct ipsec_sa *sa;
1443         struct ipsec_sa tmpl;
1444         const struct ipsec_sa *sar;
1445
1446         sar = sa_ctx->sa;
1447         if (inbound != 0)
1448                 num = nb_sa_in;
1449         else
1450                 num = nb_sa_out;
1451
1452         tmpl.spi = spi;
1453
1454         sa = bsearch(&tmpl, sar, num, sizeof(struct ipsec_sa), sa_cmp);
1455         if (sa != NULL)
1456                 return RTE_PTR_DIFF(sa, sar) / sizeof(struct ipsec_sa);
1457
1458         return -ENOENT;
1459 }
1460
1461 void
1462 sa_init(struct socket_ctx *ctx, int32_t socket_id)
1463 {
1464         int32_t rc;
1465         const char *name;
1466
1467         if (ctx == NULL)
1468                 rte_exit(EXIT_FAILURE, "NULL context.\n");
1469
1470         if (ctx->sa_in != NULL)
1471                 rte_exit(EXIT_FAILURE, "Inbound SA DB for socket %u already "
1472                                 "initialized\n", socket_id);
1473
1474         if (ctx->sa_out != NULL)
1475                 rte_exit(EXIT_FAILURE, "Outbound SA DB for socket %u already "
1476                                 "initialized\n", socket_id);
1477
1478         if (nb_sa_in > 0) {
1479                 name = "sa_in";
1480                 ctx->sa_in = sa_create(name, socket_id, nb_sa_in);
1481                 if (ctx->sa_in == NULL)
1482                         rte_exit(EXIT_FAILURE, "Error [%d] creating SA "
1483                                 "context %s in socket %d\n", rte_errno,
1484                                 name, socket_id);
1485
1486                 rc = ipsec_sad_create(name, &ctx->sa_in->sad, socket_id,
1487                                 &sa_in_cnt);
1488                 if (rc != 0)
1489                         rte_exit(EXIT_FAILURE, "failed to init SAD\n");
1490
1491                 sa_in_add_rules(ctx->sa_in, sa_in, nb_sa_in, ctx);
1492
1493                 if (app_sa_prm.enable != 0) {
1494                         rc = ipsec_satbl_init(ctx->sa_in, nb_sa_in,
1495                                 socket_id);
1496                         if (rc != 0)
1497                                 rte_exit(EXIT_FAILURE,
1498                                         "failed to init inbound SAs\n");
1499                 }
1500         } else
1501                 RTE_LOG(WARNING, IPSEC, "No SA Inbound rule specified\n");
1502
1503         if (nb_sa_out > 0) {
1504                 name = "sa_out";
1505                 ctx->sa_out = sa_create(name, socket_id, nb_sa_out);
1506                 if (ctx->sa_out == NULL)
1507                         rte_exit(EXIT_FAILURE, "Error [%d] creating SA "
1508                                 "context %s in socket %d\n", rte_errno,
1509                                 name, socket_id);
1510
1511                 sa_out_add_rules(ctx->sa_out, sa_out, nb_sa_out, ctx);
1512
1513                 if (app_sa_prm.enable != 0) {
1514                         rc = ipsec_satbl_init(ctx->sa_out, nb_sa_out,
1515                                 socket_id);
1516                         if (rc != 0)
1517                                 rte_exit(EXIT_FAILURE,
1518                                         "failed to init outbound SAs\n");
1519                 }
1520         } else
1521                 RTE_LOG(WARNING, IPSEC, "No SA Outbound rule "
1522                         "specified\n");
1523 }
1524
1525 int
1526 inbound_sa_check(struct sa_ctx *sa_ctx, struct rte_mbuf *m, uint32_t sa_idx)
1527 {
1528         struct ipsec_mbuf_metadata *priv;
1529         struct ipsec_sa *sa;
1530
1531         priv = get_priv(m);
1532         sa = priv->sa;
1533         if (sa != NULL)
1534                 return (sa_ctx->sa[sa_idx].spi == sa->spi);
1535
1536         RTE_LOG(ERR, IPSEC, "SA not saved in private data\n");
1537         return 0;
1538 }
1539
1540 void
1541 inbound_sa_lookup(struct sa_ctx *sa_ctx, struct rte_mbuf *pkts[],
1542                 void *sa_arr[], uint16_t nb_pkts)
1543 {
1544         uint32_t i;
1545         void *result_sa;
1546         struct ipsec_sa *sa;
1547
1548         sad_lookup(&sa_ctx->sad, pkts, sa_arr, nb_pkts);
1549
1550         /*
1551          * Mark need for inline offload fallback on the LSB of SA pointer.
1552          * Thanks to packet grouping mechanism which ipsec_process is using
1553          * packets marked for fallback processing will form separate group.
1554          *
1555          * Because it is not safe to use SA pointer it is casted to generic
1556          * pointer to prevent from unintentional use. Use ipsec_mask_saptr
1557          * to get valid struct pointer.
1558          */
1559         for (i = 0; i < nb_pkts; i++) {
1560                 if (sa_arr[i] == NULL)
1561                         continue;
1562
1563                 result_sa = sa = sa_arr[i];
1564                 if (MBUF_NO_SEC_OFFLOAD(pkts[i]) &&
1565                         sa->fallback_sessions > 0) {
1566                         uintptr_t intsa = (uintptr_t)sa;
1567                         intsa |= IPSEC_SA_OFFLOAD_FALLBACK_FLAG;
1568                         result_sa = (void *)intsa;
1569                 }
1570                 sa_arr[i] = result_sa;
1571         }
1572 }
1573
1574 void
1575 outbound_sa_lookup(struct sa_ctx *sa_ctx, uint32_t sa_idx[],
1576                 void *sa[], uint16_t nb_pkts)
1577 {
1578         uint32_t i;
1579
1580         for (i = 0; i < nb_pkts; i++)
1581                 sa[i] = &sa_ctx->sa[sa_idx[i]];
1582 }
1583
1584 /*
1585  * Select HW offloads to be used.
1586  */
1587 int
1588 sa_check_offloads(uint16_t port_id, uint64_t *rx_offloads,
1589                 uint64_t *tx_offloads)
1590 {
1591         struct ipsec_sa *rule;
1592         uint32_t idx_sa;
1593         enum rte_security_session_action_type rule_type;
1594
1595         *rx_offloads = 0;
1596         *tx_offloads = 0;
1597
1598         /* Check for inbound rules that use offloads and use this port */
1599         for (idx_sa = 0; idx_sa < nb_sa_in; idx_sa++) {
1600                 rule = &sa_in[idx_sa];
1601                 rule_type = ipsec_get_action_type(rule);
1602                 if ((rule_type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO ||
1603                                 rule_type ==
1604                                 RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL)
1605                                 && rule->portid == port_id)
1606                         *rx_offloads |= DEV_RX_OFFLOAD_SECURITY;
1607         }
1608
1609         /* Check for outbound rules that use offloads and use this port */
1610         for (idx_sa = 0; idx_sa < nb_sa_out; idx_sa++) {
1611                 rule = &sa_out[idx_sa];
1612                 rule_type = ipsec_get_action_type(rule);
1613                 if ((rule_type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO ||
1614                                 rule_type ==
1615                                 RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL)
1616                                 && rule->portid == port_id)
1617                         *tx_offloads |= DEV_TX_OFFLOAD_SECURITY;
1618         }
1619         return 0;
1620 }
1621
1622 void
1623 sa_sort_arr(void)
1624 {
1625         qsort(sa_in, nb_sa_in, sizeof(struct ipsec_sa), sa_cmp);
1626         qsort(sa_out, nb_sa_out, sizeof(struct ipsec_sa), sa_cmp);
1627 }