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