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