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