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