examples: use SPDX tag for Intel copyright files
[dpdk.git] / examples / ipsec-secgw / sa.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2016-2017 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
23 #include "ipsec.h"
24 #include "esp.h"
25 #include "parser.h"
26
27 #define IPDEFTTL 64
28
29 struct supported_cipher_algo {
30         const char *keyword;
31         enum rte_crypto_cipher_algorithm algo;
32         uint16_t iv_len;
33         uint16_t block_size;
34         uint16_t key_len;
35 };
36
37 struct supported_auth_algo {
38         const char *keyword;
39         enum rte_crypto_auth_algorithm algo;
40         uint16_t digest_len;
41         uint16_t key_len;
42         uint8_t key_not_req;
43 };
44
45 struct supported_aead_algo {
46         const char *keyword;
47         enum rte_crypto_aead_algorithm algo;
48         uint16_t iv_len;
49         uint16_t block_size;
50         uint16_t digest_len;
51         uint16_t key_len;
52         uint8_t aad_len;
53 };
54
55
56 const struct supported_cipher_algo cipher_algos[] = {
57         {
58                 .keyword = "null",
59                 .algo = RTE_CRYPTO_CIPHER_NULL,
60                 .iv_len = 0,
61                 .block_size = 4,
62                 .key_len = 0
63         },
64         {
65                 .keyword = "aes-128-cbc",
66                 .algo = RTE_CRYPTO_CIPHER_AES_CBC,
67                 .iv_len = 16,
68                 .block_size = 16,
69                 .key_len = 16
70         },
71         {
72                 .keyword = "aes-128-ctr",
73                 .algo = RTE_CRYPTO_CIPHER_AES_CTR,
74                 .iv_len = 8,
75                 .block_size = 16, /* XXX AESNI MB limition, should be 4 */
76                 .key_len = 20
77         }
78 };
79
80 const struct supported_auth_algo auth_algos[] = {
81         {
82                 .keyword = "null",
83                 .algo = RTE_CRYPTO_AUTH_NULL,
84                 .digest_len = 0,
85                 .key_len = 0,
86                 .key_not_req = 1
87         },
88         {
89                 .keyword = "sha1-hmac",
90                 .algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
91                 .digest_len = 12,
92                 .key_len = 20
93         },
94         {
95                 .keyword = "sha256-hmac",
96                 .algo = RTE_CRYPTO_AUTH_SHA256_HMAC,
97                 .digest_len = 12,
98                 .key_len = 32
99         }
100 };
101
102 const struct supported_aead_algo aead_algos[] = {
103         {
104                 .keyword = "aes-128-gcm",
105                 .algo = RTE_CRYPTO_AEAD_AES_GCM,
106                 .iv_len = 8,
107                 .block_size = 4,
108                 .key_len = 20,
109                 .digest_len = 16,
110                 .aad_len = 8,
111         }
112 };
113
114 struct ipsec_sa sa_out[IPSEC_SA_MAX_ENTRIES];
115 uint32_t nb_sa_out;
116
117 struct ipsec_sa sa_in[IPSEC_SA_MAX_ENTRIES];
118 uint32_t nb_sa_in;
119
120 static const struct supported_cipher_algo *
121 find_match_cipher_algo(const char *cipher_keyword)
122 {
123         size_t i;
124
125         for (i = 0; i < RTE_DIM(cipher_algos); i++) {
126                 const struct supported_cipher_algo *algo =
127                         &cipher_algos[i];
128
129                 if (strcmp(cipher_keyword, algo->keyword) == 0)
130                         return algo;
131         }
132
133         return NULL;
134 }
135
136 static const struct supported_auth_algo *
137 find_match_auth_algo(const char *auth_keyword)
138 {
139         size_t i;
140
141         for (i = 0; i < RTE_DIM(auth_algos); i++) {
142                 const struct supported_auth_algo *algo =
143                         &auth_algos[i];
144
145                 if (strcmp(auth_keyword, algo->keyword) == 0)
146                         return algo;
147         }
148
149         return NULL;
150 }
151
152 static const struct supported_aead_algo *
153 find_match_aead_algo(const char *aead_keyword)
154 {
155         size_t i;
156
157         for (i = 0; i < RTE_DIM(aead_algos); i++) {
158                 const struct supported_aead_algo *algo =
159                         &aead_algos[i];
160
161                 if (strcmp(aead_keyword, algo->keyword) == 0)
162                         return algo;
163         }
164
165         return NULL;
166 }
167
168 /** parse_key_string
169  *  parse x:x:x:x.... hex number key string into uint8_t *key
170  *  return:
171  *  > 0: number of bytes parsed
172  *  0:   failed
173  */
174 static uint32_t
175 parse_key_string(const char *key_str, uint8_t *key)
176 {
177         const char *pt_start = key_str, *pt_end = key_str;
178         uint32_t nb_bytes = 0;
179
180         while (pt_end != NULL) {
181                 char sub_str[3] = {0};
182
183                 pt_end = strchr(pt_start, ':');
184
185                 if (pt_end == NULL) {
186                         if (strlen(pt_start) > 2)
187                                 return 0;
188                         strncpy(sub_str, pt_start, 2);
189                 } else {
190                         if (pt_end - pt_start > 2)
191                                 return 0;
192
193                         strncpy(sub_str, pt_start, pt_end - pt_start);
194                         pt_start = pt_end + 1;
195                 }
196
197                 key[nb_bytes++] = strtol(sub_str, NULL, 16);
198         }
199
200         return nb_bytes;
201 }
202
203 void
204 parse_sa_tokens(char **tokens, uint32_t n_tokens,
205         struct parse_status *status)
206 {
207         struct ipsec_sa *rule = NULL;
208         uint32_t ti; /*token index*/
209         uint32_t *ri /*rule index*/;
210         uint32_t cipher_algo_p = 0;
211         uint32_t auth_algo_p = 0;
212         uint32_t aead_algo_p = 0;
213         uint32_t src_p = 0;
214         uint32_t dst_p = 0;
215         uint32_t mode_p = 0;
216         uint32_t type_p = 0;
217         uint32_t portid_p = 0;
218
219         if (strcmp(tokens[0], "in") == 0) {
220                 ri = &nb_sa_in;
221
222                 APP_CHECK(*ri <= IPSEC_SA_MAX_ENTRIES - 1, status,
223                         "too many sa rules, abort insertion\n");
224                 if (status->status < 0)
225                         return;
226
227                 rule = &sa_in[*ri];
228         } else {
229                 ri = &nb_sa_out;
230
231                 APP_CHECK(*ri <= IPSEC_SA_MAX_ENTRIES - 1, status,
232                         "too many sa rules, abort insertion\n");
233                 if (status->status < 0)
234                         return;
235
236                 rule = &sa_out[*ri];
237         }
238
239         /* spi number */
240         APP_CHECK_TOKEN_IS_NUM(tokens, 1, status);
241         if (status->status < 0)
242                 return;
243         rule->spi = atoi(tokens[1]);
244
245         for (ti = 2; ti < n_tokens; ti++) {
246                 if (strcmp(tokens[ti], "mode") == 0) {
247                         APP_CHECK_PRESENCE(mode_p, tokens[ti], status);
248                         if (status->status < 0)
249                                 return;
250
251                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
252                         if (status->status < 0)
253                                 return;
254
255                         if (strcmp(tokens[ti], "ipv4-tunnel") == 0)
256                                 rule->flags = IP4_TUNNEL;
257                         else if (strcmp(tokens[ti], "ipv6-tunnel") == 0)
258                                 rule->flags = IP6_TUNNEL;
259                         else if (strcmp(tokens[ti], "transport") == 0)
260                                 rule->flags = TRANSPORT;
261                         else {
262                                 APP_CHECK(0, status, "unrecognized "
263                                         "input \"%s\"", tokens[ti]);
264                                 return;
265                         }
266
267                         mode_p = 1;
268                         continue;
269                 }
270
271                 if (strcmp(tokens[ti], "cipher_algo") == 0) {
272                         const struct supported_cipher_algo *algo;
273                         uint32_t key_len;
274
275                         APP_CHECK_PRESENCE(cipher_algo_p, tokens[ti],
276                                 status);
277                         if (status->status < 0)
278                                 return;
279
280                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
281                         if (status->status < 0)
282                                 return;
283
284                         algo = find_match_cipher_algo(tokens[ti]);
285
286                         APP_CHECK(algo != NULL, status, "unrecognized "
287                                 "input \"%s\"", tokens[ti]);
288
289                         rule->cipher_algo = algo->algo;
290                         rule->block_size = algo->block_size;
291                         rule->iv_len = algo->iv_len;
292                         rule->cipher_key_len = algo->key_len;
293
294                         /* for NULL algorithm, no cipher key required */
295                         if (rule->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
296                                 cipher_algo_p = 1;
297                                 continue;
298                         }
299
300                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
301                         if (status->status < 0)
302                                 return;
303
304                         APP_CHECK(strcmp(tokens[ti], "cipher_key") == 0,
305                                 status, "unrecognized input \"%s\", "
306                                 "expect \"cipher_key\"", tokens[ti]);
307                         if (status->status < 0)
308                                 return;
309
310                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
311                         if (status->status < 0)
312                                 return;
313
314                         key_len = parse_key_string(tokens[ti],
315                                 rule->cipher_key);
316                         APP_CHECK(key_len == rule->cipher_key_len, status,
317                                 "unrecognized input \"%s\"", tokens[ti]);
318                         if (status->status < 0)
319                                 return;
320
321                         if (algo->algo == RTE_CRYPTO_CIPHER_AES_CBC)
322                                 rule->salt = (uint32_t)rte_rand();
323
324                         if (algo->algo == RTE_CRYPTO_CIPHER_AES_CTR) {
325                                 key_len -= 4;
326                                 rule->cipher_key_len = key_len;
327                                 memcpy(&rule->salt,
328                                         &rule->cipher_key[key_len], 4);
329                         }
330
331                         cipher_algo_p = 1;
332                         continue;
333                 }
334
335                 if (strcmp(tokens[ti], "auth_algo") == 0) {
336                         const struct supported_auth_algo *algo;
337                         uint32_t key_len;
338
339                         APP_CHECK_PRESENCE(auth_algo_p, tokens[ti],
340                                 status);
341                         if (status->status < 0)
342                                 return;
343
344                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
345                         if (status->status < 0)
346                                 return;
347
348                         algo = find_match_auth_algo(tokens[ti]);
349                         APP_CHECK(algo != NULL, status, "unrecognized "
350                                 "input \"%s\"", tokens[ti]);
351
352                         rule->auth_algo = algo->algo;
353                         rule->auth_key_len = algo->key_len;
354                         rule->digest_len = algo->digest_len;
355
356                         /* NULL algorithm and combined algos do not
357                          * require auth key
358                          */
359                         if (algo->key_not_req) {
360                                 auth_algo_p = 1;
361                                 continue;
362                         }
363
364                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
365                         if (status->status < 0)
366                                 return;
367
368                         APP_CHECK(strcmp(tokens[ti], "auth_key") == 0,
369                                 status, "unrecognized input \"%s\", "
370                                 "expect \"auth_key\"", tokens[ti]);
371                         if (status->status < 0)
372                                 return;
373
374                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
375                         if (status->status < 0)
376                                 return;
377
378                         key_len = parse_key_string(tokens[ti],
379                                 rule->auth_key);
380                         APP_CHECK(key_len == rule->auth_key_len, status,
381                                 "unrecognized input \"%s\"", tokens[ti]);
382                         if (status->status < 0)
383                                 return;
384
385                         auth_algo_p = 1;
386                         continue;
387                 }
388
389                 if (strcmp(tokens[ti], "aead_algo") == 0) {
390                         const struct supported_aead_algo *algo;
391                         uint32_t key_len;
392
393                         APP_CHECK_PRESENCE(aead_algo_p, tokens[ti],
394                                 status);
395                         if (status->status < 0)
396                                 return;
397
398                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
399                         if (status->status < 0)
400                                 return;
401
402                         algo = find_match_aead_algo(tokens[ti]);
403
404                         APP_CHECK(algo != NULL, status, "unrecognized "
405                                 "input \"%s\"", tokens[ti]);
406
407                         rule->aead_algo = algo->algo;
408                         rule->cipher_key_len = algo->key_len;
409                         rule->digest_len = algo->digest_len;
410                         rule->aad_len = algo->aad_len;
411                         rule->block_size = algo->block_size;
412                         rule->iv_len = algo->iv_len;
413
414                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
415                         if (status->status < 0)
416                                 return;
417
418                         APP_CHECK(strcmp(tokens[ti], "aead_key") == 0,
419                                 status, "unrecognized input \"%s\", "
420                                 "expect \"aead_key\"", tokens[ti]);
421                         if (status->status < 0)
422                                 return;
423
424                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
425                         if (status->status < 0)
426                                 return;
427
428                         key_len = parse_key_string(tokens[ti],
429                                 rule->cipher_key);
430                         APP_CHECK(key_len == rule->cipher_key_len, status,
431                                 "unrecognized input \"%s\"", tokens[ti]);
432                         if (status->status < 0)
433                                 return;
434
435                         key_len -= 4;
436                         rule->cipher_key_len = key_len;
437                         memcpy(&rule->salt,
438                                 &rule->cipher_key[key_len], 4);
439
440                         aead_algo_p = 1;
441                         continue;
442                 }
443
444                 if (strcmp(tokens[ti], "src") == 0) {
445                         APP_CHECK_PRESENCE(src_p, tokens[ti], status);
446                         if (status->status < 0)
447                                 return;
448
449                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
450                         if (status->status < 0)
451                                 return;
452
453                         if (rule->flags == IP4_TUNNEL) {
454                                 struct in_addr ip;
455
456                                 APP_CHECK(parse_ipv4_addr(tokens[ti],
457                                         &ip, NULL) == 0, status,
458                                         "unrecognized input \"%s\", "
459                                         "expect valid ipv4 addr",
460                                         tokens[ti]);
461                                 if (status->status < 0)
462                                         return;
463                                 rule->src.ip.ip4 = rte_bswap32(
464                                         (uint32_t)ip.s_addr);
465                         } else if (rule->flags == IP6_TUNNEL) {
466                                 struct in6_addr ip;
467
468                                 APP_CHECK(parse_ipv6_addr(tokens[ti], &ip,
469                                         NULL) == 0, status,
470                                         "unrecognized input \"%s\", "
471                                         "expect valid ipv6 addr",
472                                         tokens[ti]);
473                                 if (status->status < 0)
474                                         return;
475                                 memcpy(rule->src.ip.ip6.ip6_b,
476                                         ip.s6_addr, 16);
477                         } else if (rule->flags == TRANSPORT) {
478                                 APP_CHECK(0, status, "unrecognized input "
479                                         "\"%s\"", tokens[ti]);
480                                 return;
481                         }
482
483                         src_p = 1;
484                         continue;
485                 }
486
487                 if (strcmp(tokens[ti], "dst") == 0) {
488                         APP_CHECK_PRESENCE(dst_p, tokens[ti], status);
489                         if (status->status < 0)
490                                 return;
491
492                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
493                         if (status->status < 0)
494                                 return;
495
496                         if (rule->flags == IP4_TUNNEL) {
497                                 struct in_addr ip;
498
499                                 APP_CHECK(parse_ipv4_addr(tokens[ti],
500                                         &ip, NULL) == 0, status,
501                                         "unrecognized input \"%s\", "
502                                         "expect valid ipv4 addr",
503                                         tokens[ti]);
504                                 if (status->status < 0)
505                                         return;
506                                 rule->dst.ip.ip4 = rte_bswap32(
507                                         (uint32_t)ip.s_addr);
508                         } else if (rule->flags == IP6_TUNNEL) {
509                                 struct in6_addr ip;
510
511                                 APP_CHECK(parse_ipv6_addr(tokens[ti], &ip,
512                                         NULL) == 0, status,
513                                         "unrecognized input \"%s\", "
514                                         "expect valid ipv6 addr",
515                                         tokens[ti]);
516                                 if (status->status < 0)
517                                         return;
518                                 memcpy(rule->dst.ip.ip6.ip6_b, ip.s6_addr, 16);
519                         } else if (rule->flags == TRANSPORT) {
520                                 APP_CHECK(0, status, "unrecognized "
521                                         "input \"%s\"", tokens[ti]);
522                                 return;
523                         }
524
525                         dst_p = 1;
526                         continue;
527                 }
528
529                 if (strcmp(tokens[ti], "type") == 0) {
530                         APP_CHECK_PRESENCE(type_p, tokens[ti], status);
531                         if (status->status < 0)
532                                 return;
533
534                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
535                         if (status->status < 0)
536                                 return;
537
538                         if (strcmp(tokens[ti], "inline-crypto-offload") == 0)
539                                 rule->type =
540                                         RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO;
541                         else if (strcmp(tokens[ti],
542                                         "inline-protocol-offload") == 0)
543                                 rule->type =
544                                 RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL;
545                         else if (strcmp(tokens[ti],
546                                         "lookaside-protocol-offload") == 0)
547                                 rule->type =
548                                 RTE_SECURITY_ACTION_TYPE_LOOKASIDE_PROTOCOL;
549                         else if (strcmp(tokens[ti], "no-offload") == 0)
550                                 rule->type = RTE_SECURITY_ACTION_TYPE_NONE;
551                         else {
552                                 APP_CHECK(0, status, "Invalid input \"%s\"",
553                                                 tokens[ti]);
554                                 return;
555                         }
556
557                         type_p = 1;
558                         continue;
559                 }
560
561                 if (strcmp(tokens[ti], "port_id") == 0) {
562                         APP_CHECK_PRESENCE(portid_p, tokens[ti], status);
563                         if (status->status < 0)
564                                 return;
565                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
566                         if (status->status < 0)
567                                 return;
568                         rule->portid = atoi(tokens[ti]);
569                         if (status->status < 0)
570                                 return;
571                         portid_p = 1;
572                         continue;
573                 }
574
575                 /* unrecognizeable input */
576                 APP_CHECK(0, status, "unrecognized input \"%s\"",
577                         tokens[ti]);
578                 return;
579         }
580
581         if (aead_algo_p) {
582                 APP_CHECK(cipher_algo_p == 0, status,
583                                 "AEAD used, no need for cipher options");
584                 if (status->status < 0)
585                         return;
586
587                 APP_CHECK(auth_algo_p == 0, status,
588                                 "AEAD used, no need for auth options");
589                 if (status->status < 0)
590                         return;
591         } else {
592                 APP_CHECK(cipher_algo_p == 1, status, "missing cipher or AEAD options");
593                 if (status->status < 0)
594                         return;
595
596                 APP_CHECK(auth_algo_p == 1, status, "missing auth or AEAD options");
597                 if (status->status < 0)
598                         return;
599         }
600
601         APP_CHECK(mode_p == 1, status, "missing mode option");
602         if (status->status < 0)
603                 return;
604
605         if ((rule->type != RTE_SECURITY_ACTION_TYPE_NONE) && (portid_p == 0))
606                 printf("Missing portid option, falling back to non-offload\n");
607
608         if (!type_p || !portid_p) {
609                 rule->type = RTE_SECURITY_ACTION_TYPE_NONE;
610                 rule->portid = -1;
611         }
612
613         *ri = *ri + 1;
614 }
615
616 static inline void
617 print_one_sa_rule(const struct ipsec_sa *sa, int inbound)
618 {
619         uint32_t i;
620         uint8_t a, b, c, d;
621
622         printf("\tspi_%s(%3u):", inbound?"in":"out", sa->spi);
623
624         for (i = 0; i < RTE_DIM(cipher_algos); i++) {
625                 if (cipher_algos[i].algo == sa->cipher_algo) {
626                         printf("%s ", cipher_algos[i].keyword);
627                         break;
628                 }
629         }
630
631         for (i = 0; i < RTE_DIM(auth_algos); i++) {
632                 if (auth_algos[i].algo == sa->auth_algo) {
633                         printf("%s ", auth_algos[i].keyword);
634                         break;
635                 }
636         }
637
638         for (i = 0; i < RTE_DIM(aead_algos); i++) {
639                 if (aead_algos[i].algo == sa->aead_algo) {
640                         printf("%s ", aead_algos[i].keyword);
641                         break;
642                 }
643         }
644
645         printf("mode:");
646
647         switch (sa->flags) {
648         case IP4_TUNNEL:
649                 printf("IP4Tunnel ");
650                 uint32_t_to_char(sa->src.ip.ip4, &a, &b, &c, &d);
651                 printf("%hhu.%hhu.%hhu.%hhu ", d, c, b, a);
652                 uint32_t_to_char(sa->dst.ip.ip4, &a, &b, &c, &d);
653                 printf("%hhu.%hhu.%hhu.%hhu", d, c, b, a);
654                 break;
655         case IP6_TUNNEL:
656                 printf("IP6Tunnel ");
657                 for (i = 0; i < 16; i++) {
658                         if (i % 2 && i != 15)
659                                 printf("%.2x:", sa->src.ip.ip6.ip6_b[i]);
660                         else
661                                 printf("%.2x", sa->src.ip.ip6.ip6_b[i]);
662                 }
663                 printf(" ");
664                 for (i = 0; i < 16; i++) {
665                         if (i % 2 && i != 15)
666                                 printf("%.2x:", sa->dst.ip.ip6.ip6_b[i]);
667                         else
668                                 printf("%.2x", sa->dst.ip.ip6.ip6_b[i]);
669                 }
670                 break;
671         case TRANSPORT:
672                 printf("Transport");
673                 break;
674         }
675         printf("\n");
676 }
677
678 struct sa_ctx {
679         struct ipsec_sa sa[IPSEC_SA_MAX_ENTRIES];
680         union {
681                 struct {
682                         struct rte_crypto_sym_xform a;
683                         struct rte_crypto_sym_xform b;
684                 };
685         } xf[IPSEC_SA_MAX_ENTRIES];
686 };
687
688 static struct sa_ctx *
689 sa_create(const char *name, int32_t socket_id)
690 {
691         char s[PATH_MAX];
692         struct sa_ctx *sa_ctx;
693         uint32_t mz_size;
694         const struct rte_memzone *mz;
695
696         snprintf(s, sizeof(s), "%s_%u", name, socket_id);
697
698         /* Create SA array table */
699         printf("Creating SA context with %u maximum entries\n",
700                         IPSEC_SA_MAX_ENTRIES);
701
702         mz_size = sizeof(struct sa_ctx);
703         mz = rte_memzone_reserve(s, mz_size, socket_id,
704                         RTE_MEMZONE_1GB | RTE_MEMZONE_SIZE_HINT_ONLY);
705         if (mz == NULL) {
706                 printf("Failed to allocate SA DB memory\n");
707                 rte_errno = -ENOMEM;
708                 return NULL;
709         }
710
711         sa_ctx = (struct sa_ctx *)mz->addr;
712
713         return sa_ctx;
714 }
715
716 static int
717 check_eth_dev_caps(uint16_t portid, uint32_t inbound)
718 {
719         struct rte_eth_dev_info dev_info;
720
721         rte_eth_dev_info_get(portid, &dev_info);
722
723         if (inbound) {
724                 if ((dev_info.rx_offload_capa &
725                                 DEV_RX_OFFLOAD_SECURITY) == 0) {
726                         RTE_LOG(WARNING, PORT,
727                                 "hardware RX IPSec offload is not supported\n");
728                         return -EINVAL;
729                 }
730
731         } else { /* outbound */
732                 if ((dev_info.tx_offload_capa &
733                                 DEV_TX_OFFLOAD_SECURITY) == 0) {
734                         RTE_LOG(WARNING, PORT,
735                                 "hardware TX IPSec offload is not supported\n");
736                         return -EINVAL;
737                 }
738         }
739         return 0;
740 }
741
742
743 static int
744 sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
745                 uint32_t nb_entries, uint32_t inbound)
746 {
747         struct ipsec_sa *sa;
748         uint32_t i, idx;
749         uint16_t iv_length;
750
751         for (i = 0; i < nb_entries; i++) {
752                 idx = SPI2IDX(entries[i].spi);
753                 sa = &sa_ctx->sa[idx];
754                 if (sa->spi != 0) {
755                         printf("Index %u already in use by SPI %u\n",
756                                         idx, sa->spi);
757                         return -EINVAL;
758                 }
759                 *sa = entries[i];
760                 sa->seq = 0;
761
762                 if (sa->type == RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL ||
763                         sa->type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO) {
764                         if (check_eth_dev_caps(sa->portid, inbound))
765                                 return -EINVAL;
766                 }
767
768                 sa->direction = (inbound == 1) ?
769                                 RTE_SECURITY_IPSEC_SA_DIR_INGRESS :
770                                 RTE_SECURITY_IPSEC_SA_DIR_EGRESS;
771
772                 switch (sa->flags) {
773                 case IP4_TUNNEL:
774                         sa->src.ip.ip4 = rte_cpu_to_be_32(sa->src.ip.ip4);
775                         sa->dst.ip.ip4 = rte_cpu_to_be_32(sa->dst.ip.ip4);
776                 }
777
778                 if (sa->aead_algo == RTE_CRYPTO_AEAD_AES_GCM) {
779                         iv_length = 16;
780
781                         sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_AEAD;
782                         sa_ctx->xf[idx].a.aead.algo = sa->aead_algo;
783                         sa_ctx->xf[idx].a.aead.key.data = sa->cipher_key;
784                         sa_ctx->xf[idx].a.aead.key.length =
785                                 sa->cipher_key_len;
786                         sa_ctx->xf[idx].a.aead.op = (inbound == 1) ?
787                                 RTE_CRYPTO_AEAD_OP_DECRYPT :
788                                 RTE_CRYPTO_AEAD_OP_ENCRYPT;
789                         sa_ctx->xf[idx].a.next = NULL;
790                         sa_ctx->xf[idx].a.aead.iv.offset = IV_OFFSET;
791                         sa_ctx->xf[idx].a.aead.iv.length = iv_length;
792                         sa_ctx->xf[idx].a.aead.aad_length =
793                                 sa->aad_len;
794                         sa_ctx->xf[idx].a.aead.digest_length =
795                                 sa->digest_len;
796
797                         sa->xforms = &sa_ctx->xf[idx].a;
798
799                         print_one_sa_rule(sa, inbound);
800                 } else {
801                         switch (sa->cipher_algo) {
802                         case RTE_CRYPTO_CIPHER_NULL:
803                         case RTE_CRYPTO_CIPHER_AES_CBC:
804                                 iv_length = sa->iv_len;
805                                 break;
806                         case RTE_CRYPTO_CIPHER_AES_CTR:
807                                 iv_length = 16;
808                                 break;
809                         default:
810                                 RTE_LOG(ERR, IPSEC_ESP,
811                                                 "unsupported cipher algorithm %u\n",
812                                                 sa->cipher_algo);
813                                 return -EINVAL;
814                         }
815
816                         if (inbound) {
817                                 sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
818                                 sa_ctx->xf[idx].b.cipher.algo = sa->cipher_algo;
819                                 sa_ctx->xf[idx].b.cipher.key.data = sa->cipher_key;
820                                 sa_ctx->xf[idx].b.cipher.key.length =
821                                         sa->cipher_key_len;
822                                 sa_ctx->xf[idx].b.cipher.op =
823                                         RTE_CRYPTO_CIPHER_OP_DECRYPT;
824                                 sa_ctx->xf[idx].b.next = NULL;
825                                 sa_ctx->xf[idx].b.cipher.iv.offset = IV_OFFSET;
826                                 sa_ctx->xf[idx].b.cipher.iv.length = iv_length;
827
828                                 sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_AUTH;
829                                 sa_ctx->xf[idx].a.auth.algo = sa->auth_algo;
830                                 sa_ctx->xf[idx].a.auth.key.data = sa->auth_key;
831                                 sa_ctx->xf[idx].a.auth.key.length =
832                                         sa->auth_key_len;
833                                 sa_ctx->xf[idx].a.auth.digest_length =
834                                         sa->digest_len;
835                                 sa_ctx->xf[idx].a.auth.op =
836                                         RTE_CRYPTO_AUTH_OP_VERIFY;
837                         } else { /* outbound */
838                                 sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
839                                 sa_ctx->xf[idx].a.cipher.algo = sa->cipher_algo;
840                                 sa_ctx->xf[idx].a.cipher.key.data = sa->cipher_key;
841                                 sa_ctx->xf[idx].a.cipher.key.length =
842                                         sa->cipher_key_len;
843                                 sa_ctx->xf[idx].a.cipher.op =
844                                         RTE_CRYPTO_CIPHER_OP_ENCRYPT;
845                                 sa_ctx->xf[idx].a.next = NULL;
846                                 sa_ctx->xf[idx].a.cipher.iv.offset = IV_OFFSET;
847                                 sa_ctx->xf[idx].a.cipher.iv.length = iv_length;
848
849                                 sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_AUTH;
850                                 sa_ctx->xf[idx].b.auth.algo = sa->auth_algo;
851                                 sa_ctx->xf[idx].b.auth.key.data = sa->auth_key;
852                                 sa_ctx->xf[idx].b.auth.key.length =
853                                         sa->auth_key_len;
854                                 sa_ctx->xf[idx].b.auth.digest_length =
855                                         sa->digest_len;
856                                 sa_ctx->xf[idx].b.auth.op =
857                                         RTE_CRYPTO_AUTH_OP_GENERATE;
858                         }
859
860                         sa_ctx->xf[idx].a.next = &sa_ctx->xf[idx].b;
861                         sa_ctx->xf[idx].b.next = NULL;
862                         sa->xforms = &sa_ctx->xf[idx].a;
863
864                         print_one_sa_rule(sa, inbound);
865                 }
866         }
867
868         return 0;
869 }
870
871 static inline int
872 sa_out_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
873                 uint32_t nb_entries)
874 {
875         return sa_add_rules(sa_ctx, entries, nb_entries, 0);
876 }
877
878 static inline int
879 sa_in_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
880                 uint32_t nb_entries)
881 {
882         return sa_add_rules(sa_ctx, entries, nb_entries, 1);
883 }
884
885 void
886 sa_init(struct socket_ctx *ctx, int32_t socket_id)
887 {
888         const char *name;
889
890         if (ctx == NULL)
891                 rte_exit(EXIT_FAILURE, "NULL context.\n");
892
893         if (ctx->sa_in != NULL)
894                 rte_exit(EXIT_FAILURE, "Inbound SA DB for socket %u already "
895                                 "initialized\n", socket_id);
896
897         if (ctx->sa_out != NULL)
898                 rte_exit(EXIT_FAILURE, "Outbound SA DB for socket %u already "
899                                 "initialized\n", socket_id);
900
901         if (nb_sa_in > 0) {
902                 name = "sa_in";
903                 ctx->sa_in = sa_create(name, socket_id);
904                 if (ctx->sa_in == NULL)
905                         rte_exit(EXIT_FAILURE, "Error [%d] creating SA "
906                                 "context %s in socket %d\n", rte_errno,
907                                 name, socket_id);
908
909                 sa_in_add_rules(ctx->sa_in, sa_in, nb_sa_in);
910         } else
911                 RTE_LOG(WARNING, IPSEC, "No SA Inbound rule specified\n");
912
913         if (nb_sa_out > 0) {
914                 name = "sa_out";
915                 ctx->sa_out = sa_create(name, socket_id);
916                 if (ctx->sa_out == NULL)
917                         rte_exit(EXIT_FAILURE, "Error [%d] creating SA "
918                                 "context %s in socket %d\n", rte_errno,
919                                 name, socket_id);
920
921                 sa_out_add_rules(ctx->sa_out, sa_out, nb_sa_out);
922         } else
923                 RTE_LOG(WARNING, IPSEC, "No SA Outbound rule "
924                         "specified\n");
925 }
926
927 int
928 inbound_sa_check(struct sa_ctx *sa_ctx, struct rte_mbuf *m, uint32_t sa_idx)
929 {
930         struct ipsec_mbuf_metadata *priv;
931
932         priv = RTE_PTR_ADD(m, sizeof(struct rte_mbuf));
933
934         return (sa_ctx->sa[sa_idx].spi == priv->sa->spi);
935 }
936
937 static inline void
938 single_inbound_lookup(struct ipsec_sa *sadb, struct rte_mbuf *pkt,
939                 struct ipsec_sa **sa_ret)
940 {
941         struct esp_hdr *esp;
942         struct ip *ip;
943         uint32_t *src4_addr;
944         uint8_t *src6_addr;
945         struct ipsec_sa *sa;
946
947         *sa_ret = NULL;
948
949         ip = rte_pktmbuf_mtod(pkt, struct ip *);
950         if (ip->ip_v == IPVERSION)
951                 esp = (struct esp_hdr *)(ip + 1);
952         else
953                 esp = (struct esp_hdr *)(((struct ip6_hdr *)ip) + 1);
954
955         if (esp->spi == INVALID_SPI)
956                 return;
957
958         sa = &sadb[SPI2IDX(rte_be_to_cpu_32(esp->spi))];
959         if (rte_be_to_cpu_32(esp->spi) != sa->spi)
960                 return;
961
962         switch (sa->flags) {
963         case IP4_TUNNEL:
964                 src4_addr = RTE_PTR_ADD(ip, offsetof(struct ip, ip_src));
965                 if ((ip->ip_v == IPVERSION) &&
966                                 (sa->src.ip.ip4 == *src4_addr) &&
967                                 (sa->dst.ip.ip4 == *(src4_addr + 1)))
968                         *sa_ret = sa;
969                 break;
970         case IP6_TUNNEL:
971                 src6_addr = RTE_PTR_ADD(ip, offsetof(struct ip6_hdr, ip6_src));
972                 if ((ip->ip_v == IP6_VERSION) &&
973                                 !memcmp(&sa->src.ip.ip6.ip6, src6_addr, 16) &&
974                                 !memcmp(&sa->dst.ip.ip6.ip6, src6_addr + 16, 16))
975                         *sa_ret = sa;
976                 break;
977         case TRANSPORT:
978                 *sa_ret = sa;
979         }
980 }
981
982 void
983 inbound_sa_lookup(struct sa_ctx *sa_ctx, struct rte_mbuf *pkts[],
984                 struct ipsec_sa *sa[], uint16_t nb_pkts)
985 {
986         uint32_t i;
987
988         for (i = 0; i < nb_pkts; i++)
989                 single_inbound_lookup(sa_ctx->sa, pkts[i], &sa[i]);
990 }
991
992 void
993 outbound_sa_lookup(struct sa_ctx *sa_ctx, uint32_t sa_idx[],
994                 struct ipsec_sa *sa[], uint16_t nb_pkts)
995 {
996         uint32_t i;
997
998         for (i = 0; i < nb_pkts; i++)
999                 sa[i] = &sa_ctx->sa[sa_idx[i]];
1000 }