xref: /dpdk/examples/ipsec-secgw/sa.c (revision a4f56123)
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 = 16, /* XXX AESNI MB limition, should be 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 struct ipsec_sa sa_out[IPSEC_SA_MAX_ENTRIES];
129 uint32_t nb_sa_out;
130 
131 struct ipsec_sa sa_in[IPSEC_SA_MAX_ENTRIES];
132 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 inline 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("\n");
694 }
695 
696 struct sa_ctx {
697 	struct ipsec_sa sa[IPSEC_SA_MAX_ENTRIES];
698 	union {
699 		struct {
700 			struct rte_crypto_sym_xform a;
701 			struct rte_crypto_sym_xform b;
702 		};
703 	} xf[IPSEC_SA_MAX_ENTRIES];
704 };
705 
706 static struct sa_ctx *
707 sa_create(const char *name, int32_t socket_id)
708 {
709 	char s[PATH_MAX];
710 	struct sa_ctx *sa_ctx;
711 	uint32_t mz_size;
712 	const struct rte_memzone *mz;
713 
714 	snprintf(s, sizeof(s), "%s_%u", name, socket_id);
715 
716 	/* Create SA array table */
717 	printf("Creating SA context with %u maximum entries\n",
718 			IPSEC_SA_MAX_ENTRIES);
719 
720 	mz_size = sizeof(struct sa_ctx);
721 	mz = rte_memzone_reserve(s, mz_size, socket_id,
722 			RTE_MEMZONE_1GB | RTE_MEMZONE_SIZE_HINT_ONLY);
723 	if (mz == NULL) {
724 		printf("Failed to allocate SA DB memory\n");
725 		rte_errno = -ENOMEM;
726 		return NULL;
727 	}
728 
729 	sa_ctx = (struct sa_ctx *)mz->addr;
730 
731 	return sa_ctx;
732 }
733 
734 static int
735 check_eth_dev_caps(uint16_t portid, uint32_t inbound)
736 {
737 	struct rte_eth_dev_info dev_info;
738 
739 	rte_eth_dev_info_get(portid, &dev_info);
740 
741 	if (inbound) {
742 		if ((dev_info.rx_offload_capa &
743 				DEV_RX_OFFLOAD_SECURITY) == 0) {
744 			RTE_LOG(WARNING, PORT,
745 				"hardware RX IPSec offload is not supported\n");
746 			return -EINVAL;
747 		}
748 
749 	} else { /* outbound */
750 		if ((dev_info.tx_offload_capa &
751 				DEV_TX_OFFLOAD_SECURITY) == 0) {
752 			RTE_LOG(WARNING, PORT,
753 				"hardware TX IPSec offload is not supported\n");
754 			return -EINVAL;
755 		}
756 	}
757 	return 0;
758 }
759 
760 
761 static int
762 sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
763 		uint32_t nb_entries, uint32_t inbound)
764 {
765 	struct ipsec_sa *sa;
766 	uint32_t i, idx;
767 	uint16_t iv_length;
768 
769 	for (i = 0; i < nb_entries; i++) {
770 		idx = SPI2IDX(entries[i].spi);
771 		sa = &sa_ctx->sa[idx];
772 		if (sa->spi != 0) {
773 			printf("Index %u already in use by SPI %u\n",
774 					idx, sa->spi);
775 			return -EINVAL;
776 		}
777 		*sa = entries[i];
778 		sa->seq = 0;
779 
780 		if (sa->type == RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL ||
781 			sa->type == RTE_SECURITY_ACTION_TYPE_INLINE_CRYPTO) {
782 			if (check_eth_dev_caps(sa->portid, inbound))
783 				return -EINVAL;
784 		}
785 
786 		sa->direction = (inbound == 1) ?
787 				RTE_SECURITY_IPSEC_SA_DIR_INGRESS :
788 				RTE_SECURITY_IPSEC_SA_DIR_EGRESS;
789 
790 		switch (sa->flags) {
791 		case IP4_TUNNEL:
792 			sa->src.ip.ip4 = rte_cpu_to_be_32(sa->src.ip.ip4);
793 			sa->dst.ip.ip4 = rte_cpu_to_be_32(sa->dst.ip.ip4);
794 		}
795 
796 		if (sa->aead_algo == RTE_CRYPTO_AEAD_AES_GCM) {
797 			iv_length = 16;
798 
799 			sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_AEAD;
800 			sa_ctx->xf[idx].a.aead.algo = sa->aead_algo;
801 			sa_ctx->xf[idx].a.aead.key.data = sa->cipher_key;
802 			sa_ctx->xf[idx].a.aead.key.length =
803 				sa->cipher_key_len;
804 			sa_ctx->xf[idx].a.aead.op = (inbound == 1) ?
805 				RTE_CRYPTO_AEAD_OP_DECRYPT :
806 				RTE_CRYPTO_AEAD_OP_ENCRYPT;
807 			sa_ctx->xf[idx].a.next = NULL;
808 			sa_ctx->xf[idx].a.aead.iv.offset = IV_OFFSET;
809 			sa_ctx->xf[idx].a.aead.iv.length = iv_length;
810 			sa_ctx->xf[idx].a.aead.aad_length =
811 				sa->aad_len;
812 			sa_ctx->xf[idx].a.aead.digest_length =
813 				sa->digest_len;
814 
815 			sa->xforms = &sa_ctx->xf[idx].a;
816 
817 			print_one_sa_rule(sa, inbound);
818 		} else {
819 			switch (sa->cipher_algo) {
820 			case RTE_CRYPTO_CIPHER_NULL:
821 			case RTE_CRYPTO_CIPHER_3DES_CBC:
822 			case RTE_CRYPTO_CIPHER_AES_CBC:
823 				iv_length = sa->iv_len;
824 				break;
825 			case RTE_CRYPTO_CIPHER_AES_CTR:
826 				iv_length = 16;
827 				break;
828 			default:
829 				RTE_LOG(ERR, IPSEC_ESP,
830 						"unsupported cipher algorithm %u\n",
831 						sa->cipher_algo);
832 				return -EINVAL;
833 			}
834 
835 			if (inbound) {
836 				sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
837 				sa_ctx->xf[idx].b.cipher.algo = sa->cipher_algo;
838 				sa_ctx->xf[idx].b.cipher.key.data = sa->cipher_key;
839 				sa_ctx->xf[idx].b.cipher.key.length =
840 					sa->cipher_key_len;
841 				sa_ctx->xf[idx].b.cipher.op =
842 					RTE_CRYPTO_CIPHER_OP_DECRYPT;
843 				sa_ctx->xf[idx].b.next = NULL;
844 				sa_ctx->xf[idx].b.cipher.iv.offset = IV_OFFSET;
845 				sa_ctx->xf[idx].b.cipher.iv.length = iv_length;
846 
847 				sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_AUTH;
848 				sa_ctx->xf[idx].a.auth.algo = sa->auth_algo;
849 				sa_ctx->xf[idx].a.auth.key.data = sa->auth_key;
850 				sa_ctx->xf[idx].a.auth.key.length =
851 					sa->auth_key_len;
852 				sa_ctx->xf[idx].a.auth.digest_length =
853 					sa->digest_len;
854 				sa_ctx->xf[idx].a.auth.op =
855 					RTE_CRYPTO_AUTH_OP_VERIFY;
856 			} else { /* outbound */
857 				sa_ctx->xf[idx].a.type = RTE_CRYPTO_SYM_XFORM_CIPHER;
858 				sa_ctx->xf[idx].a.cipher.algo = sa->cipher_algo;
859 				sa_ctx->xf[idx].a.cipher.key.data = sa->cipher_key;
860 				sa_ctx->xf[idx].a.cipher.key.length =
861 					sa->cipher_key_len;
862 				sa_ctx->xf[idx].a.cipher.op =
863 					RTE_CRYPTO_CIPHER_OP_ENCRYPT;
864 				sa_ctx->xf[idx].a.next = NULL;
865 				sa_ctx->xf[idx].a.cipher.iv.offset = IV_OFFSET;
866 				sa_ctx->xf[idx].a.cipher.iv.length = iv_length;
867 
868 				sa_ctx->xf[idx].b.type = RTE_CRYPTO_SYM_XFORM_AUTH;
869 				sa_ctx->xf[idx].b.auth.algo = sa->auth_algo;
870 				sa_ctx->xf[idx].b.auth.key.data = sa->auth_key;
871 				sa_ctx->xf[idx].b.auth.key.length =
872 					sa->auth_key_len;
873 				sa_ctx->xf[idx].b.auth.digest_length =
874 					sa->digest_len;
875 				sa_ctx->xf[idx].b.auth.op =
876 					RTE_CRYPTO_AUTH_OP_GENERATE;
877 			}
878 
879 			sa_ctx->xf[idx].a.next = &sa_ctx->xf[idx].b;
880 			sa_ctx->xf[idx].b.next = NULL;
881 			sa->xforms = &sa_ctx->xf[idx].a;
882 
883 			print_one_sa_rule(sa, inbound);
884 		}
885 	}
886 
887 	return 0;
888 }
889 
890 static inline int
891 sa_out_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
892 		uint32_t nb_entries)
893 {
894 	return sa_add_rules(sa_ctx, entries, nb_entries, 0);
895 }
896 
897 static inline int
898 sa_in_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
899 		uint32_t nb_entries)
900 {
901 	return sa_add_rules(sa_ctx, entries, nb_entries, 1);
902 }
903 
904 void
905 sa_init(struct socket_ctx *ctx, int32_t socket_id)
906 {
907 	const char *name;
908 
909 	if (ctx == NULL)
910 		rte_exit(EXIT_FAILURE, "NULL context.\n");
911 
912 	if (ctx->sa_in != NULL)
913 		rte_exit(EXIT_FAILURE, "Inbound SA DB for socket %u already "
914 				"initialized\n", socket_id);
915 
916 	if (ctx->sa_out != NULL)
917 		rte_exit(EXIT_FAILURE, "Outbound SA DB for socket %u already "
918 				"initialized\n", socket_id);
919 
920 	if (nb_sa_in > 0) {
921 		name = "sa_in";
922 		ctx->sa_in = sa_create(name, socket_id);
923 		if (ctx->sa_in == NULL)
924 			rte_exit(EXIT_FAILURE, "Error [%d] creating SA "
925 				"context %s in socket %d\n", rte_errno,
926 				name, socket_id);
927 
928 		sa_in_add_rules(ctx->sa_in, sa_in, nb_sa_in);
929 	} else
930 		RTE_LOG(WARNING, IPSEC, "No SA Inbound rule specified\n");
931 
932 	if (nb_sa_out > 0) {
933 		name = "sa_out";
934 		ctx->sa_out = sa_create(name, socket_id);
935 		if (ctx->sa_out == NULL)
936 			rte_exit(EXIT_FAILURE, "Error [%d] creating SA "
937 				"context %s in socket %d\n", rte_errno,
938 				name, socket_id);
939 
940 		sa_out_add_rules(ctx->sa_out, sa_out, nb_sa_out);
941 	} else
942 		RTE_LOG(WARNING, IPSEC, "No SA Outbound rule "
943 			"specified\n");
944 }
945 
946 int
947 inbound_sa_check(struct sa_ctx *sa_ctx, struct rte_mbuf *m, uint32_t sa_idx)
948 {
949 	struct ipsec_mbuf_metadata *priv;
950 
951 	priv = get_priv(m);
952 
953 	return (sa_ctx->sa[sa_idx].spi == priv->sa->spi);
954 }
955 
956 static inline void
957 single_inbound_lookup(struct ipsec_sa *sadb, struct rte_mbuf *pkt,
958 		struct ipsec_sa **sa_ret)
959 {
960 	struct esp_hdr *esp;
961 	struct ip *ip;
962 	uint32_t *src4_addr;
963 	uint8_t *src6_addr;
964 	struct ipsec_sa *sa;
965 
966 	*sa_ret = NULL;
967 
968 	ip = rte_pktmbuf_mtod(pkt, struct ip *);
969 	if (ip->ip_v == IPVERSION)
970 		esp = (struct esp_hdr *)(ip + 1);
971 	else
972 		esp = (struct esp_hdr *)(((struct ip6_hdr *)ip) + 1);
973 
974 	if (esp->spi == INVALID_SPI)
975 		return;
976 
977 	sa = &sadb[SPI2IDX(rte_be_to_cpu_32(esp->spi))];
978 	if (rte_be_to_cpu_32(esp->spi) != sa->spi)
979 		return;
980 
981 	switch (sa->flags) {
982 	case IP4_TUNNEL:
983 		src4_addr = RTE_PTR_ADD(ip, offsetof(struct ip, ip_src));
984 		if ((ip->ip_v == IPVERSION) &&
985 				(sa->src.ip.ip4 == *src4_addr) &&
986 				(sa->dst.ip.ip4 == *(src4_addr + 1)))
987 			*sa_ret = sa;
988 		break;
989 	case IP6_TUNNEL:
990 		src6_addr = RTE_PTR_ADD(ip, offsetof(struct ip6_hdr, ip6_src));
991 		if ((ip->ip_v == IP6_VERSION) &&
992 				!memcmp(&sa->src.ip.ip6.ip6, src6_addr, 16) &&
993 				!memcmp(&sa->dst.ip.ip6.ip6, src6_addr + 16, 16))
994 			*sa_ret = sa;
995 		break;
996 	case TRANSPORT:
997 		*sa_ret = sa;
998 	}
999 }
1000 
1001 void
1002 inbound_sa_lookup(struct sa_ctx *sa_ctx, struct rte_mbuf *pkts[],
1003 		struct ipsec_sa *sa[], uint16_t nb_pkts)
1004 {
1005 	uint32_t i;
1006 
1007 	for (i = 0; i < nb_pkts; i++)
1008 		single_inbound_lookup(sa_ctx->sa, pkts[i], &sa[i]);
1009 }
1010 
1011 void
1012 outbound_sa_lookup(struct sa_ctx *sa_ctx, uint32_t sa_idx[],
1013 		struct ipsec_sa *sa[], uint16_t nb_pkts)
1014 {
1015 	uint32_t i;
1016 
1017 	for (i = 0; i < nb_pkts; i++)
1018 		sa[i] = &sa_ctx->sa[sa_idx[i]];
1019 }
1020