examples/ipsec-secgw: support security offload
[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         rule->spi = atoi(tokens[1]);
273
274         for (ti = 2; ti < n_tokens; ti++) {
275                 if (strcmp(tokens[ti], "mode") == 0) {
276                         APP_CHECK_PRESENCE(mode_p, tokens[ti], 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                         if (strcmp(tokens[ti], "ipv4-tunnel") == 0)
285                                 rule->flags = IP4_TUNNEL;
286                         else if (strcmp(tokens[ti], "ipv6-tunnel") == 0)
287                                 rule->flags = IP6_TUNNEL;
288                         else if (strcmp(tokens[ti], "transport") == 0)
289                                 rule->flags = TRANSPORT;
290                         else {
291                                 APP_CHECK(0, status, "unrecognized "
292                                         "input \"%s\"", tokens[ti]);
293                                 return;
294                         }
295
296                         mode_p = 1;
297                         continue;
298                 }
299
300                 if (strcmp(tokens[ti], "cipher_algo") == 0) {
301                         const struct supported_cipher_algo *algo;
302                         uint32_t key_len;
303
304                         APP_CHECK_PRESENCE(cipher_algo_p, tokens[ti],
305                                 status);
306                         if (status->status < 0)
307                                 return;
308
309                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
310                         if (status->status < 0)
311                                 return;
312
313                         algo = find_match_cipher_algo(tokens[ti]);
314
315                         APP_CHECK(algo != NULL, status, "unrecognized "
316                                 "input \"%s\"", tokens[ti]);
317
318                         rule->cipher_algo = algo->algo;
319                         rule->block_size = algo->block_size;
320                         rule->iv_len = algo->iv_len;
321                         rule->cipher_key_len = algo->key_len;
322
323                         /* for NULL algorithm, no cipher key required */
324                         if (rule->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
325                                 cipher_algo_p = 1;
326                                 continue;
327                         }
328
329                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
330                         if (status->status < 0)
331                                 return;
332
333                         APP_CHECK(strcmp(tokens[ti], "cipher_key") == 0,
334                                 status, "unrecognized input \"%s\", "
335                                 "expect \"cipher_key\"", tokens[ti]);
336                         if (status->status < 0)
337                                 return;
338
339                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
340                         if (status->status < 0)
341                                 return;
342
343                         key_len = parse_key_string(tokens[ti],
344                                 rule->cipher_key);
345                         APP_CHECK(key_len == rule->cipher_key_len, status,
346                                 "unrecognized input \"%s\"", tokens[ti]);
347                         if (status->status < 0)
348                                 return;
349
350                         if (algo->algo == RTE_CRYPTO_CIPHER_AES_CBC)
351                                 rule->salt = (uint32_t)rte_rand();
352
353                         if (algo->algo == RTE_CRYPTO_CIPHER_AES_CTR) {
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
385                         /* NULL algorithm and combined algos do not
386                          * require auth key
387                          */
388                         if (algo->key_not_req) {
389                                 auth_algo_p = 1;
390                                 continue;
391                         }
392
393                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
394                         if (status->status < 0)
395                                 return;
396
397                         APP_CHECK(strcmp(tokens[ti], "auth_key") == 0,
398                                 status, "unrecognized input \"%s\", "
399                                 "expect \"auth_key\"", tokens[ti]);
400                         if (status->status < 0)
401                                 return;
402
403                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
404                         if (status->status < 0)
405                                 return;
406
407                         key_len = parse_key_string(tokens[ti],
408                                 rule->auth_key);
409                         APP_CHECK(key_len == rule->auth_key_len, status,
410                                 "unrecognized input \"%s\"", tokens[ti]);
411                         if (status->status < 0)
412                                 return;
413
414                         auth_algo_p = 1;
415                         continue;
416                 }
417
418                 if (strcmp(tokens[ti], "aead_algo") == 0) {
419                         const struct supported_aead_algo *algo;
420                         uint32_t key_len;
421
422                         APP_CHECK_PRESENCE(aead_algo_p, tokens[ti],
423                                 status);
424                         if (status->status < 0)
425                                 return;
426
427                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
428                         if (status->status < 0)
429                                 return;
430
431                         algo = find_match_aead_algo(tokens[ti]);
432
433                         APP_CHECK(algo != NULL, status, "unrecognized "
434                                 "input \"%s\"", tokens[ti]);
435
436                         rule->aead_algo = algo->algo;
437                         rule->cipher_key_len = algo->key_len;
438                         rule->digest_len = algo->digest_len;
439                         rule->aad_len = algo->aad_len;
440                         rule->block_size = algo->block_size;
441                         rule->iv_len = algo->iv_len;
442
443                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
444                         if (status->status < 0)
445                                 return;
446
447                         APP_CHECK(strcmp(tokens[ti], "aead_key") == 0,
448                                 status, "unrecognized input \"%s\", "
449                                 "expect \"aead_key\"", tokens[ti]);
450                         if (status->status < 0)
451                                 return;
452
453                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
454                         if (status->status < 0)
455                                 return;
456
457                         key_len = parse_key_string(tokens[ti],
458                                 rule->cipher_key);
459                         APP_CHECK(key_len == rule->cipher_key_len, status,
460                                 "unrecognized input \"%s\"", tokens[ti]);
461                         if (status->status < 0)
462                                 return;
463
464                         key_len -= 4;
465                         rule->cipher_key_len = key_len;
466                         memcpy(&rule->salt,
467                                 &rule->cipher_key[key_len], 4);
468
469                         aead_algo_p = 1;
470                         continue;
471                 }
472
473                 if (strcmp(tokens[ti], "src") == 0) {
474                         APP_CHECK_PRESENCE(src_p, tokens[ti], status);
475                         if (status->status < 0)
476                                 return;
477
478                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
479                         if (status->status < 0)
480                                 return;
481
482                         if (rule->flags == IP4_TUNNEL) {
483                                 struct in_addr ip;
484
485                                 APP_CHECK(parse_ipv4_addr(tokens[ti],
486                                         &ip, NULL) == 0, status,
487                                         "unrecognized input \"%s\", "
488                                         "expect valid ipv4 addr",
489                                         tokens[ti]);
490                                 if (status->status < 0)
491                                         return;
492                                 rule->src.ip.ip4 = rte_bswap32(
493                                         (uint32_t)ip.s_addr);
494                         } else if (rule->flags == IP6_TUNNEL) {
495                                 struct in6_addr ip;
496
497                                 APP_CHECK(parse_ipv6_addr(tokens[ti], &ip,
498                                         NULL) == 0, status,
499                                         "unrecognized input \"%s\", "
500                                         "expect valid ipv6 addr",
501                                         tokens[ti]);
502                                 if (status->status < 0)
503                                         return;
504                                 memcpy(rule->src.ip.ip6.ip6_b,
505                                         ip.s6_addr, 16);
506                         } else if (rule->flags == TRANSPORT) {
507                                 APP_CHECK(0, status, "unrecognized input "
508                                         "\"%s\"", tokens[ti]);
509                                 return;
510                         }
511
512                         src_p = 1;
513                         continue;
514                 }
515
516                 if (strcmp(tokens[ti], "dst") == 0) {
517                         APP_CHECK_PRESENCE(dst_p, tokens[ti], status);
518                         if (status->status < 0)
519                                 return;
520
521                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
522                         if (status->status < 0)
523                                 return;
524
525                         if (rule->flags == IP4_TUNNEL) {
526                                 struct in_addr ip;
527
528                                 APP_CHECK(parse_ipv4_addr(tokens[ti],
529                                         &ip, NULL) == 0, status,
530                                         "unrecognized input \"%s\", "
531                                         "expect valid ipv4 addr",
532                                         tokens[ti]);
533                                 if (status->status < 0)
534                                         return;
535                                 rule->dst.ip.ip4 = rte_bswap32(
536                                         (uint32_t)ip.s_addr);
537                         } else if (rule->flags == IP6_TUNNEL) {
538                                 struct in6_addr ip;
539
540                                 APP_CHECK(parse_ipv6_addr(tokens[ti], &ip,
541                                         NULL) == 0, status,
542                                         "unrecognized input \"%s\", "
543                                         "expect valid ipv6 addr",
544                                         tokens[ti]);
545                                 if (status->status < 0)
546                                         return;
547                                 memcpy(rule->dst.ip.ip6.ip6_b, ip.s6_addr, 16);
548                         } else if (rule->flags == TRANSPORT) {
549                                 APP_CHECK(0, status, "unrecognized "
550                                         "input \"%s\"", tokens[ti]);
551                                 return;
552                         }
553
554                         dst_p = 1;
555                         continue;
556                 }
557
558                 if (strcmp(tokens[ti], "type") == 0) {
559                         APP_CHECK_PRESENCE(type_p, tokens[ti], status);
560                         if (status->status < 0)
561                                 return;
562
563                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
564                         if (status->status < 0)
565                                 return;
566
567                         if (strcmp(tokens[ti], "inline-crypto-offload") == 0)
568                                 rule->type =
569                                         RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO;
570                         else if (strcmp(tokens[ti],
571                                         "inline-protocol-offload") == 0)
572                                 rule->type =
573                                 RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL;
574                         else if (strcmp(tokens[ti],
575                                         "lookaside-protocol-offload") == 0)
576                                 rule->type =
577                                 RTE_SECURITY_ACTION_TYPE_LOOKASIDE_PROTOCOL;
578                         else if (strcmp(tokens[ti], "no-offload") == 0)
579                                 rule->type = RTE_SECURITY_ACTION_TYPE_NONE;
580                         else {
581                                 APP_CHECK(0, status, "Invalid input \"%s\"",
582                                                 tokens[ti]);
583                                 return;
584                         }
585
586                         type_p = 1;
587                         continue;
588                 }
589
590                 if (strcmp(tokens[ti], "port_id") == 0) {
591                         APP_CHECK_PRESENCE(portid_p, tokens[ti], status);
592                         if (status->status < 0)
593                                 return;
594                         INCREMENT_TOKEN_INDEX(ti, n_tokens, status);
595                         if (status->status < 0)
596                                 return;
597                         rule->portid = atoi(tokens[ti]);
598                         if (status->status < 0)
599                                 return;
600                         portid_p = 1;
601                         continue;
602                 }
603
604                 /* unrecognizeable input */
605                 APP_CHECK(0, status, "unrecognized input \"%s\"",
606                         tokens[ti]);
607                 return;
608         }
609
610         if (aead_algo_p) {
611                 APP_CHECK(cipher_algo_p == 0, status,
612                                 "AEAD used, no need for cipher options");
613                 if (status->status < 0)
614                         return;
615
616                 APP_CHECK(auth_algo_p == 0, status,
617                                 "AEAD used, no need for auth options");
618                 if (status->status < 0)
619                         return;
620         } else {
621                 APP_CHECK(cipher_algo_p == 1, status, "missing cipher or AEAD options");
622                 if (status->status < 0)
623                         return;
624
625                 APP_CHECK(auth_algo_p == 1, status, "missing auth or AEAD options");
626                 if (status->status < 0)
627                         return;
628         }
629
630         APP_CHECK(mode_p == 1, status, "missing mode option");
631         if (status->status < 0)
632                 return;
633
634         if ((rule->type != RTE_SECURITY_ACTION_TYPE_NONE) && (portid_p == 0))
635                 printf("Missing portid option, falling back to non-offload\n");
636
637         if (!type_p || !portid_p) {
638                 rule->type = RTE_SECURITY_ACTION_TYPE_NONE;
639                 rule->portid = -1;
640         }
641
642         *ri = *ri + 1;
643 }
644
645 static inline void
646 print_one_sa_rule(const struct ipsec_sa *sa, int inbound)
647 {
648         uint32_t i;
649         uint8_t a, b, c, d;
650
651         printf("\tspi_%s(%3u):", inbound?"in":"out", sa->spi);
652
653         for (i = 0; i < RTE_DIM(cipher_algos); i++) {
654                 if (cipher_algos[i].algo == sa->cipher_algo) {
655                         printf("%s ", cipher_algos[i].keyword);
656                         break;
657                 }
658         }
659
660         for (i = 0; i < RTE_DIM(auth_algos); i++) {
661                 if (auth_algos[i].algo == sa->auth_algo) {
662                         printf("%s ", auth_algos[i].keyword);
663                         break;
664                 }
665         }
666
667         for (i = 0; i < RTE_DIM(aead_algos); i++) {
668                 if (aead_algos[i].algo == sa->aead_algo) {
669                         printf("%s ", aead_algos[i].keyword);
670                         break;
671                 }
672         }
673
674         printf("mode:");
675
676         switch (sa->flags) {
677         case IP4_TUNNEL:
678                 printf("IP4Tunnel ");
679                 uint32_t_to_char(sa->src.ip.ip4, &a, &b, &c, &d);
680                 printf("%hhu.%hhu.%hhu.%hhu ", d, c, b, a);
681                 uint32_t_to_char(sa->dst.ip.ip4, &a, &b, &c, &d);
682                 printf("%hhu.%hhu.%hhu.%hhu", d, c, b, a);
683                 break;
684         case IP6_TUNNEL:
685                 printf("IP6Tunnel ");
686                 for (i = 0; i < 16; i++) {
687                         if (i % 2 && i != 15)
688                                 printf("%.2x:", sa->src.ip.ip6.ip6_b[i]);
689                         else
690                                 printf("%.2x", sa->src.ip.ip6.ip6_b[i]);
691                 }
692                 printf(" ");
693                 for (i = 0; i < 16; i++) {
694                         if (i % 2 && i != 15)
695                                 printf("%.2x:", sa->dst.ip.ip6.ip6_b[i]);
696                         else
697                                 printf("%.2x", sa->dst.ip.ip6.ip6_b[i]);
698                 }
699                 break;
700         case TRANSPORT:
701                 printf("Transport");
702                 break;
703         }
704         printf("\n");
705 }
706
707 struct sa_ctx {
708         struct ipsec_sa sa[IPSEC_SA_MAX_ENTRIES];
709         union {
710                 struct {
711                         struct rte_crypto_sym_xform a;
712                         struct rte_crypto_sym_xform b;
713                 };
714         } xf[IPSEC_SA_MAX_ENTRIES];
715 };
716
717 static struct sa_ctx *
718 sa_create(const char *name, int32_t socket_id)
719 {
720         char s[PATH_MAX];
721         struct sa_ctx *sa_ctx;
722         uint32_t mz_size;
723         const struct rte_memzone *mz;
724
725         snprintf(s, sizeof(s), "%s_%u", name, socket_id);
726
727         /* Create SA array table */
728         printf("Creating SA context with %u maximum entries\n",
729                         IPSEC_SA_MAX_ENTRIES);
730
731         mz_size = sizeof(struct sa_ctx);
732         mz = rte_memzone_reserve(s, mz_size, socket_id,
733                         RTE_MEMZONE_1GB | RTE_MEMZONE_SIZE_HINT_ONLY);
734         if (mz == NULL) {
735                 printf("Failed to allocate SA DB memory\n");
736                 rte_errno = -ENOMEM;
737                 return NULL;
738         }
739
740         sa_ctx = (struct sa_ctx *)mz->addr;
741
742         return sa_ctx;
743 }
744
745 static int
746 check_eth_dev_caps(uint16_t portid, uint32_t inbound)
747 {
748         struct rte_eth_dev_info dev_info;
749
750         rte_eth_dev_info_get(portid, &dev_info);
751
752         if (inbound) {
753                 if ((dev_info.rx_offload_capa &
754                                 DEV_RX_OFFLOAD_SECURITY) == 0) {
755                         RTE_LOG(WARNING, PORT,
756                                 "hardware RX IPSec offload is not supported\n");
757                         return -EINVAL;
758                 }
759
760         } else { /* outbound */
761                 if ((dev_info.tx_offload_capa &
762                                 DEV_TX_OFFLOAD_SECURITY) == 0) {
763                         RTE_LOG(WARNING, PORT,
764                                 "hardware TX IPSec offload is not supported\n");
765                         return -EINVAL;
766                 }
767         }
768         return 0;
769 }
770
771
772 static int
773 sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
774                 uint32_t nb_entries, uint32_t inbound)
775 {
776         struct ipsec_sa *sa;
777         uint32_t i, idx;
778         uint16_t iv_length;
779
780         for (i = 0; i < nb_entries; i++) {
781                 idx = SPI2IDX(entries[i].spi);
782                 sa = &sa_ctx->sa[idx];
783                 if (sa->spi != 0) {
784                         printf("Index %u already in use by SPI %u\n",
785                                         idx, sa->spi);
786                         return -EINVAL;
787                 }
788                 *sa = entries[i];
789                 sa->seq = 0;
790
791                 if (sa->type == RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL ||
792                         sa->type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO) {
793                         if (check_eth_dev_caps(sa->portid, inbound))
794                                 return -EINVAL;
795                 }
796
797                 sa->direction = (inbound == 1) ?
798                                 RTE_SECURITY_IPSEC_SA_DIR_INGRESS :
799                                 RTE_SECURITY_IPSEC_SA_DIR_EGRESS;
800
801                 switch (sa->flags) {
802                 case IP4_TUNNEL:
803                         sa->src.ip.ip4 = rte_cpu_to_be_32(sa->src.ip.ip4);
804                         sa->dst.ip.ip4 = rte_cpu_to_be_32(sa->dst.ip.ip4);
805                 }
806
807                 if (sa->aead_algo == RTE_CRYPTO_AEAD_AES_GCM) {
808                         iv_length = 16;
809
810                         sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_AEAD;
811                         sa_ctx->xf[idx].a.aead.algo = sa->aead_algo;
812                         sa_ctx->xf[idx].a.aead.key.data = sa->cipher_key;
813                         sa_ctx->xf[idx].a.aead.key.length =
814                                 sa->cipher_key_len;
815                         sa_ctx->xf[idx].a.aead.op = (inbound == 1) ?
816                                 RTE_CRYPTO_AEAD_OP_DECRYPT :
817                                 RTE_CRYPTO_AEAD_OP_ENCRYPT;
818                         sa_ctx->xf[idx].a.next = NULL;
819                         sa_ctx->xf[idx].a.aead.iv.offset = IV_OFFSET;
820                         sa_ctx->xf[idx].a.aead.iv.length = iv_length;
821                         sa_ctx->xf[idx].a.aead.aad_length =
822                                 sa->aad_len;
823                         sa_ctx->xf[idx].a.aead.digest_length =
824                                 sa->digest_len;
825
826                         sa->xforms = &sa_ctx->xf[idx].a;
827
828                         print_one_sa_rule(sa, inbound);
829                 } else {
830                         switch (sa->cipher_algo) {
831                         case RTE_CRYPTO_CIPHER_NULL:
832                         case RTE_CRYPTO_CIPHER_AES_CBC:
833                                 iv_length = sa->iv_len;
834                                 break;
835                         case RTE_CRYPTO_CIPHER_AES_CTR:
836                                 iv_length = 16;
837                                 break;
838                         default:
839                                 RTE_LOG(ERR, IPSEC_ESP,
840                                                 "unsupported cipher algorithm %u\n",
841                                                 sa->cipher_algo);
842                                 return -EINVAL;
843                         }
844
845                         if (inbound) {
846                                 sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
847                                 sa_ctx->xf[idx].b.cipher.algo = sa->cipher_algo;
848                                 sa_ctx->xf[idx].b.cipher.key.data = sa->cipher_key;
849                                 sa_ctx->xf[idx].b.cipher.key.length =
850                                         sa->cipher_key_len;
851                                 sa_ctx->xf[idx].b.cipher.op =
852                                         RTE_CRYPTO_CIPHER_OP_DECRYPT;
853                                 sa_ctx->xf[idx].b.next = NULL;
854                                 sa_ctx->xf[idx].b.cipher.iv.offset = IV_OFFSET;
855                                 sa_ctx->xf[idx].b.cipher.iv.length = iv_length;
856
857                                 sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_AUTH;
858                                 sa_ctx->xf[idx].a.auth.algo = sa->auth_algo;
859                                 sa_ctx->xf[idx].a.auth.key.data = sa->auth_key;
860                                 sa_ctx->xf[idx].a.auth.key.length =
861                                         sa->auth_key_len;
862                                 sa_ctx->xf[idx].a.auth.digest_length =
863                                         sa->digest_len;
864                                 sa_ctx->xf[idx].a.auth.op =
865                                         RTE_CRYPTO_AUTH_OP_VERIFY;
866                         } else { /* outbound */
867                                 sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
868                                 sa_ctx->xf[idx].a.cipher.algo = sa->cipher_algo;
869                                 sa_ctx->xf[idx].a.cipher.key.data = sa->cipher_key;
870                                 sa_ctx->xf[idx].a.cipher.key.length =
871                                         sa->cipher_key_len;
872                                 sa_ctx->xf[idx].a.cipher.op =
873                                         RTE_CRYPTO_CIPHER_OP_ENCRYPT;
874                                 sa_ctx->xf[idx].a.next = NULL;
875                                 sa_ctx->xf[idx].a.cipher.iv.offset = IV_OFFSET;
876                                 sa_ctx->xf[idx].a.cipher.iv.length = iv_length;
877
878                                 sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_AUTH;
879                                 sa_ctx->xf[idx].b.auth.algo = sa->auth_algo;
880                                 sa_ctx->xf[idx].b.auth.key.data = sa->auth_key;
881                                 sa_ctx->xf[idx].b.auth.key.length =
882                                         sa->auth_key_len;
883                                 sa_ctx->xf[idx].b.auth.digest_length =
884                                         sa->digest_len;
885                                 sa_ctx->xf[idx].b.auth.op =
886                                         RTE_CRYPTO_AUTH_OP_GENERATE;
887                         }
888
889                         sa_ctx->xf[idx].a.next = &sa_ctx->xf[idx].b;
890                         sa_ctx->xf[idx].b.next = NULL;
891                         sa->xforms = &sa_ctx->xf[idx].a;
892
893                         print_one_sa_rule(sa, inbound);
894                 }
895         }
896
897         return 0;
898 }
899
900 static inline int
901 sa_out_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
902                 uint32_t nb_entries)
903 {
904         return sa_add_rules(sa_ctx, entries, nb_entries, 0);
905 }
906
907 static inline int
908 sa_in_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
909                 uint32_t nb_entries)
910 {
911         return sa_add_rules(sa_ctx, entries, nb_entries, 1);
912 }
913
914 void
915 sa_init(struct socket_ctx *ctx, int32_t socket_id)
916 {
917         const char *name;
918
919         if (ctx == NULL)
920                 rte_exit(EXIT_FAILURE, "NULL context.\n");
921
922         if (ctx->sa_in != NULL)
923                 rte_exit(EXIT_FAILURE, "Inbound SA DB for socket %u already "
924                                 "initialized\n", socket_id);
925
926         if (ctx->sa_out != NULL)
927                 rte_exit(EXIT_FAILURE, "Outbound SA DB for socket %u already "
928                                 "initialized\n", socket_id);
929
930         if (nb_sa_in > 0) {
931                 name = "sa_in";
932                 ctx->sa_in = sa_create(name, socket_id);
933                 if (ctx->sa_in == NULL)
934                         rte_exit(EXIT_FAILURE, "Error [%d] creating SA "
935                                 "context %s in socket %d\n", rte_errno,
936                                 name, socket_id);
937
938                 sa_in_add_rules(ctx->sa_in, sa_in, nb_sa_in);
939         } else
940                 RTE_LOG(WARNING, IPSEC, "No SA Inbound rule specified\n");
941
942         if (nb_sa_out > 0) {
943                 name = "sa_out";
944                 ctx->sa_out = sa_create(name, socket_id);
945                 if (ctx->sa_out == NULL)
946                         rte_exit(EXIT_FAILURE, "Error [%d] creating SA "
947                                 "context %s in socket %d\n", rte_errno,
948                                 name, socket_id);
949
950                 sa_out_add_rules(ctx->sa_out, sa_out, nb_sa_out);
951         } else
952                 RTE_LOG(WARNING, IPSEC, "No SA Outbound rule "
953                         "specified\n");
954 }
955
956 int
957 inbound_sa_check(struct sa_ctx *sa_ctx, struct rte_mbuf *m, uint32_t sa_idx)
958 {
959         struct ipsec_mbuf_metadata *priv;
960
961         priv = RTE_PTR_ADD(m, sizeof(struct rte_mbuf));
962
963         return (sa_ctx->sa[sa_idx].spi == priv->sa->spi);
964 }
965
966 static inline void
967 single_inbound_lookup(struct ipsec_sa *sadb, struct rte_mbuf *pkt,
968                 struct ipsec_sa **sa_ret)
969 {
970         struct esp_hdr *esp;
971         struct ip *ip;
972         uint32_t *src4_addr;
973         uint8_t *src6_addr;
974         struct ipsec_sa *sa;
975
976         *sa_ret = NULL;
977
978         ip = rte_pktmbuf_mtod(pkt, struct ip *);
979         if (ip->ip_v == IPVERSION)
980                 esp = (struct esp_hdr *)(ip + 1);
981         else
982                 esp = (struct esp_hdr *)(((struct ip6_hdr *)ip) + 1);
983
984         if (esp->spi == INVALID_SPI)
985                 return;
986
987         sa = &sadb[SPI2IDX(rte_be_to_cpu_32(esp->spi))];
988         if (rte_be_to_cpu_32(esp->spi) != sa->spi)
989                 return;
990
991         switch (sa->flags) {
992         case IP4_TUNNEL:
993                 src4_addr = RTE_PTR_ADD(ip, offsetof(struct ip, ip_src));
994                 if ((ip->ip_v == IPVERSION) &&
995                                 (sa->src.ip.ip4 == *src4_addr) &&
996                                 (sa->dst.ip.ip4 == *(src4_addr + 1)))
997                         *sa_ret = sa;
998                 break;
999         case IP6_TUNNEL:
1000                 src6_addr = RTE_PTR_ADD(ip, offsetof(struct ip6_hdr, ip6_src));
1001                 if ((ip->ip_v == IP6_VERSION) &&
1002                                 !memcmp(&sa->src.ip.ip6.ip6, src6_addr, 16) &&
1003                                 !memcmp(&sa->dst.ip.ip6.ip6, src6_addr + 16, 16))
1004                         *sa_ret = sa;
1005                 break;
1006         case TRANSPORT:
1007                 *sa_ret = sa;
1008         }
1009 }
1010
1011 void
1012 inbound_sa_lookup(struct sa_ctx *sa_ctx, struct rte_mbuf *pkts[],
1013                 struct ipsec_sa *sa[], uint16_t nb_pkts)
1014 {
1015         uint32_t i;
1016
1017         for (i = 0; i < nb_pkts; i++)
1018                 single_inbound_lookup(sa_ctx->sa, pkts[i], &sa[i]);
1019 }
1020
1021 void
1022 outbound_sa_lookup(struct sa_ctx *sa_ctx, uint32_t sa_idx[],
1023                 struct ipsec_sa *sa[], uint16_t nb_pkts)
1024 {
1025         uint32_t i;
1026
1027         for (i = 0; i < nb_pkts; i++)
1028                 sa[i] = &sa_ctx->sa[sa_idx[i]];
1029 }