xref: /dpdk/examples/ipsec-secgw/sa.c (revision ceb1ccd5d50c1a89ba8bdd97cc199e7f07422b98)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2016 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * Security Associations
36  */
37 #include <netinet/ip.h>
38 
39 #include <rte_memzone.h>
40 #include <rte_crypto.h>
41 #include <rte_cryptodev.h>
42 #include <rte_byteorder.h>
43 #include <rte_errno.h>
44 
45 #include "ipsec.h"
46 #include "esp.h"
47 
48 /* SAs EP0 Outbound */
49 const struct ipsec_sa sa_ep0_out[] = {
50 	{ 5, 0, IPv4(172, 16, 1, 5), IPv4(172, 16, 2, 5),
51 		NULL, NULL,
52 		esp4_tunnel_outbound_pre_crypto,
53 		esp4_tunnel_outbound_post_crypto,
54 		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
55 		12, 16, 16,
56 		0, 0 },
57 	{ 6, 0, IPv4(172, 16, 1, 6), IPv4(172, 16, 2, 6),
58 		NULL, NULL,
59 		esp4_tunnel_outbound_pre_crypto,
60 		esp4_tunnel_outbound_post_crypto,
61 		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
62 		12, 16, 16,
63 		0, 0 },
64 	{ 7, 0, IPv4(172, 16, 1, 7), IPv4(172, 16, 2, 7),
65 		NULL, NULL,
66 		esp4_tunnel_outbound_pre_crypto,
67 		esp4_tunnel_outbound_post_crypto,
68 		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
69 		12, 16, 16,
70 		0, 0 },
71 	{ 8, 0, IPv4(172, 16, 1, 8), IPv4(172, 16, 2, 8),
72 		NULL, NULL,
73 		esp4_tunnel_outbound_pre_crypto,
74 		esp4_tunnel_outbound_post_crypto,
75 		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
76 		12, 16, 16,
77 		0, 0 },
78 	{ 9, 0, IPv4(172, 16, 1, 5), IPv4(172, 16, 2, 5),
79 		NULL, NULL,
80 		esp4_tunnel_outbound_pre_crypto,
81 		esp4_tunnel_outbound_post_crypto,
82 		RTE_CRYPTO_CIPHER_NULL, RTE_CRYPTO_AUTH_NULL,
83 		0, 0, 4,
84 		0, 0 },
85 };
86 
87 /* SAs EP0 Inbound */
88 const struct ipsec_sa sa_ep0_in[] = {
89 	{ 5, 0, IPv4(172, 16, 2, 5), IPv4(172, 16, 1, 5),
90 		NULL, NULL,
91 		esp4_tunnel_inbound_pre_crypto,
92 		esp4_tunnel_inbound_post_crypto,
93 		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
94 		12, 16, 16,
95 		0, 0 },
96 	{ 6, 0, IPv4(172, 16, 2, 6), IPv4(172, 16, 1, 6),
97 		NULL, NULL,
98 		esp4_tunnel_inbound_pre_crypto,
99 		esp4_tunnel_inbound_post_crypto,
100 		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
101 		12, 16, 16,
102 		0, 0 },
103 	{ 7, 0, IPv4(172, 16, 2, 7), IPv4(172, 16, 1, 7),
104 		NULL, NULL,
105 		esp4_tunnel_inbound_pre_crypto,
106 		esp4_tunnel_inbound_post_crypto,
107 		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
108 		12, 16, 16,
109 		0, 0 },
110 	{ 8, 0, IPv4(172, 16, 2, 8), IPv4(172, 16, 1, 8),
111 		NULL, NULL,
112 		esp4_tunnel_inbound_pre_crypto,
113 		esp4_tunnel_inbound_post_crypto,
114 		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
115 		12, 16, 16,
116 		0, 0 },
117 	{ 9, 0, IPv4(172, 16, 2, 5), IPv4(172, 16, 1, 5),
118 		NULL, NULL,
119 		esp4_tunnel_inbound_pre_crypto,
120 		esp4_tunnel_inbound_post_crypto,
121 		RTE_CRYPTO_CIPHER_NULL, RTE_CRYPTO_AUTH_NULL,
122 		0, 0, 4,
123 		0, 0 },
124 };
125 
126 /* SAs EP1 Outbound */
127 const struct ipsec_sa sa_ep1_out[] = {
128 	{ 5, 0, IPv4(172, 16, 2, 5), IPv4(172, 16, 1, 5),
129 		NULL, NULL,
130 		esp4_tunnel_outbound_pre_crypto,
131 		esp4_tunnel_outbound_post_crypto,
132 		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
133 		12, 16, 16,
134 		0, 0 },
135 	{ 6, 0, IPv4(172, 16, 2, 6), IPv4(172, 16, 1, 6),
136 		NULL, NULL,
137 		esp4_tunnel_outbound_pre_crypto,
138 		esp4_tunnel_outbound_post_crypto,
139 		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
140 		12, 16, 16,
141 		0, 0 },
142 	{ 7, 0, IPv4(172, 16, 2, 7), IPv4(172, 16, 1, 7),
143 		NULL, NULL,
144 		esp4_tunnel_outbound_pre_crypto,
145 		esp4_tunnel_outbound_post_crypto,
146 		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
147 		12, 16, 16,
148 		0, 0 },
149 	{ 8, 0, IPv4(172, 16, 2, 8), IPv4(172, 16, 1, 8),
150 		NULL, NULL,
151 		esp4_tunnel_outbound_pre_crypto,
152 		esp4_tunnel_outbound_post_crypto,
153 		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
154 		12, 16, 16,
155 		0, 0 },
156 	{ 9, 0, IPv4(172, 16, 2, 5), IPv4(172, 16, 1, 5),
157 		NULL, NULL,
158 		esp4_tunnel_outbound_pre_crypto,
159 		esp4_tunnel_outbound_post_crypto,
160 		RTE_CRYPTO_CIPHER_NULL, RTE_CRYPTO_AUTH_NULL,
161 		0, 0, 4,
162 		0, 0 },
163 };
164 
165 /* SAs EP1 Inbound */
166 const struct ipsec_sa sa_ep1_in[] = {
167 	{ 5, 0, IPv4(172, 16, 1, 5), IPv4(172, 16, 2, 5),
168 		NULL, NULL,
169 		esp4_tunnel_inbound_pre_crypto,
170 		esp4_tunnel_inbound_post_crypto,
171 		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
172 		12, 16, 16,
173 		0, 0 },
174 	{ 6, 0, IPv4(172, 16, 1, 6), IPv4(172, 16, 2, 6),
175 		NULL, NULL,
176 		esp4_tunnel_inbound_pre_crypto,
177 		esp4_tunnel_inbound_post_crypto,
178 		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
179 		12, 16, 16,
180 		0, 0 },
181 	{ 7, 0, IPv4(172, 16, 1, 7), IPv4(172, 16, 2, 7),
182 		NULL, NULL,
183 		esp4_tunnel_inbound_pre_crypto,
184 		esp4_tunnel_inbound_post_crypto,
185 		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
186 		12, 16, 16,
187 		0, 0 },
188 	{ 8, 0, IPv4(172, 16, 1, 8), IPv4(172, 16, 2, 8),
189 		NULL, NULL,
190 		esp4_tunnel_inbound_pre_crypto,
191 		esp4_tunnel_inbound_post_crypto,
192 		RTE_CRYPTO_CIPHER_AES_CBC, RTE_CRYPTO_AUTH_SHA1_HMAC,
193 		12, 16, 16,
194 		0, 0 },
195 	{ 9, 0, IPv4(172, 16, 1, 5), IPv4(172, 16, 2, 5),
196 		NULL, NULL,
197 		esp4_tunnel_inbound_pre_crypto,
198 		esp4_tunnel_inbound_post_crypto,
199 		RTE_CRYPTO_CIPHER_NULL, RTE_CRYPTO_AUTH_NULL,
200 		0, 0, 4,
201 		0, 0 },
202 };
203 
204 static uint8_t cipher_key[256] = "sixteenbytes key";
205 
206 /* AES CBC xform */
207 const struct rte_crypto_sym_xform aescbc_enc_xf = {
208 	NULL,
209 	RTE_CRYPTO_SYM_XFORM_CIPHER,
210 	.cipher = { RTE_CRYPTO_CIPHER_OP_ENCRYPT, RTE_CRYPTO_CIPHER_AES_CBC,
211 		.key = { cipher_key, 16 } }
212 };
213 
214 const struct rte_crypto_sym_xform aescbc_dec_xf = {
215 	NULL,
216 	RTE_CRYPTO_SYM_XFORM_CIPHER,
217 	.cipher = { RTE_CRYPTO_CIPHER_OP_DECRYPT, RTE_CRYPTO_CIPHER_AES_CBC,
218 		.key = { cipher_key, 16 } }
219 };
220 
221 static uint8_t auth_key[256] = "twentybytes hash key";
222 
223 /* SHA1 HMAC xform */
224 const struct rte_crypto_sym_xform sha1hmac_gen_xf = {
225 	NULL,
226 	RTE_CRYPTO_SYM_XFORM_AUTH,
227 	.auth = { RTE_CRYPTO_AUTH_OP_GENERATE, RTE_CRYPTO_AUTH_SHA1_HMAC,
228 		.key = { auth_key, 20 }, 12, 0 }
229 };
230 
231 const struct rte_crypto_sym_xform sha1hmac_verify_xf = {
232 	NULL,
233 	RTE_CRYPTO_SYM_XFORM_AUTH,
234 	.auth = { RTE_CRYPTO_AUTH_OP_VERIFY, RTE_CRYPTO_AUTH_SHA1_HMAC,
235 		.key = { auth_key, 20 }, 12, 0 }
236 };
237 
238 /* AES CBC xform */
239 const struct rte_crypto_sym_xform null_cipher_xf = {
240 	NULL,
241 	RTE_CRYPTO_SYM_XFORM_CIPHER,
242 	.cipher = { .algo = RTE_CRYPTO_CIPHER_NULL }
243 };
244 
245 const struct rte_crypto_sym_xform null_auth_xf = {
246 	NULL,
247 	RTE_CRYPTO_SYM_XFORM_AUTH,
248 	.auth = { .algo = RTE_CRYPTO_AUTH_NULL }
249 };
250 
251 struct sa_ctx {
252 	struct ipsec_sa sa[IPSEC_SA_MAX_ENTRIES];
253 	struct {
254 		struct rte_crypto_sym_xform a;
255 		struct rte_crypto_sym_xform b;
256 	} xf[IPSEC_SA_MAX_ENTRIES];
257 };
258 
259 static struct sa_ctx *
260 sa_ipv4_create(const char *name, int socket_id)
261 {
262 	char s[PATH_MAX];
263 	struct sa_ctx *sa_ctx;
264 	unsigned mz_size;
265 	const struct rte_memzone *mz;
266 
267 	snprintf(s, sizeof(s), "%s_%u", name, socket_id);
268 
269 	/* Create SA array table */
270 	printf("Creating SA context with %u maximum entries\n",
271 			IPSEC_SA_MAX_ENTRIES);
272 
273 	mz_size = sizeof(struct sa_ctx);
274 	mz = rte_memzone_reserve(s, mz_size, socket_id,
275 			RTE_MEMZONE_1GB | RTE_MEMZONE_SIZE_HINT_ONLY);
276 	if (mz == NULL) {
277 		printf("Failed to allocate SA DB memory\n");
278 		rte_errno = -ENOMEM;
279 		return NULL;
280 	}
281 
282 	sa_ctx = (struct sa_ctx *)mz->addr;
283 
284 	return sa_ctx;
285 }
286 
287 static int
288 sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
289 		unsigned nb_entries, unsigned inbound)
290 {
291 	struct ipsec_sa *sa;
292 	unsigned i, idx;
293 
294 	for (i = 0; i < nb_entries; i++) {
295 		idx = SPI2IDX(entries[i].spi);
296 		sa = &sa_ctx->sa[idx];
297 		if (sa->spi != 0) {
298 			printf("Index %u already in use by SPI %u\n",
299 					idx, sa->spi);
300 			return -EINVAL;
301 		}
302 		*sa = entries[i];
303 		sa->src = rte_cpu_to_be_32(sa->src);
304 		sa->dst = rte_cpu_to_be_32(sa->dst);
305 		if (inbound) {
306 			if (sa->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
307 				sa_ctx->xf[idx].a = null_auth_xf;
308 				sa_ctx->xf[idx].b = null_cipher_xf;
309 			} else {
310 				sa_ctx->xf[idx].a = sha1hmac_verify_xf;
311 				sa_ctx->xf[idx].b = aescbc_dec_xf;
312 			}
313 		} else { /* outbound */
314 			if (sa->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
315 				sa_ctx->xf[idx].a = null_cipher_xf;
316 				sa_ctx->xf[idx].b = null_auth_xf;
317 			} else {
318 				sa_ctx->xf[idx].a = aescbc_enc_xf;
319 				sa_ctx->xf[idx].b = sha1hmac_gen_xf;
320 			}
321 		}
322 		sa_ctx->xf[idx].a.next = &sa_ctx->xf[idx].b;
323 		sa_ctx->xf[idx].b.next = NULL;
324 		sa->xforms = &sa_ctx->xf[idx].a;
325 	}
326 
327 	return 0;
328 }
329 
330 static inline int
331 sa_out_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
332 		unsigned nb_entries)
333 {
334 	return sa_add_rules(sa_ctx, entries, nb_entries, 0);
335 }
336 
337 static inline int
338 sa_in_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
339 		unsigned nb_entries)
340 {
341 	return sa_add_rules(sa_ctx, entries, nb_entries, 1);
342 }
343 
344 void
345 sa_init(struct socket_ctx *ctx, int socket_id, unsigned ep)
346 {
347 	const struct ipsec_sa *sa_out_entries, *sa_in_entries;
348 	unsigned nb_out_entries, nb_in_entries;
349 	const char *name;
350 
351 	if (ctx == NULL)
352 		rte_exit(EXIT_FAILURE, "NULL context.\n");
353 
354 	if (ctx->sa_ipv4_in != NULL)
355 		rte_exit(EXIT_FAILURE, "Inbound SA DB for socket %u already "
356 				"initialized\n", socket_id);
357 
358 	if (ctx->sa_ipv4_out != NULL)
359 		rte_exit(EXIT_FAILURE, "Outbound SA DB for socket %u already "
360 				"initialized\n", socket_id);
361 
362 	if (ep == 0) {
363 		sa_out_entries = sa_ep0_out;
364 		nb_out_entries = RTE_DIM(sa_ep0_out);
365 		sa_in_entries = sa_ep0_in;
366 		nb_in_entries = RTE_DIM(sa_ep0_in);
367 	} else if (ep == 1) {
368 		sa_out_entries = sa_ep1_out;
369 		nb_out_entries = RTE_DIM(sa_ep1_out);
370 		sa_in_entries = sa_ep1_in;
371 		nb_in_entries = RTE_DIM(sa_ep1_in);
372 	} else
373 		rte_exit(EXIT_FAILURE, "Invalid EP value %u. "
374 				"Only 0 or 1 supported.\n", ep);
375 
376 	name = "sa_ipv4_in";
377 	ctx->sa_ipv4_in = sa_ipv4_create(name, socket_id);
378 	if (ctx->sa_ipv4_in == NULL)
379 		rte_exit(EXIT_FAILURE, "Error [%d] creating SA context %s "
380 				"in socket %d\n", rte_errno, name, socket_id);
381 
382 	name = "sa_ipv4_out";
383 	ctx->sa_ipv4_out = sa_ipv4_create(name, socket_id);
384 	if (ctx->sa_ipv4_out == NULL)
385 		rte_exit(EXIT_FAILURE, "Error [%d] creating SA context %s "
386 				"in socket %d\n", rte_errno, name, socket_id);
387 
388 	sa_in_add_rules(ctx->sa_ipv4_in, sa_in_entries, nb_in_entries);
389 
390 	sa_out_add_rules(ctx->sa_ipv4_out, sa_out_entries, nb_out_entries);
391 }
392 
393 int
394 inbound_sa_check(struct sa_ctx *sa_ctx, struct rte_mbuf *m, uint32_t sa_idx)
395 {
396 	struct ipsec_mbuf_metadata *priv;
397 
398 	priv = RTE_PTR_ADD(m, sizeof(struct rte_mbuf));
399 
400 	return (sa_ctx->sa[sa_idx].spi == priv->sa->spi);
401 }
402 
403 void
404 inbound_sa_lookup(struct sa_ctx *sa_ctx, struct rte_mbuf *pkts[],
405 		struct ipsec_sa *sa[], uint16_t nb_pkts)
406 {
407 	unsigned i;
408 	uint32_t *src, spi;
409 
410 	for (i = 0; i < nb_pkts; i++) {
411 		spi = rte_pktmbuf_mtod_offset(pkts[i], struct esp_hdr *,
412 				sizeof(struct ip))->spi;
413 
414 		if (spi == INVALID_SPI)
415 			continue;
416 
417 		sa[i] = &sa_ctx->sa[SPI2IDX(spi)];
418 		if (spi != sa[i]->spi) {
419 			sa[i] = NULL;
420 			continue;
421 		}
422 
423 		src = rte_pktmbuf_mtod_offset(pkts[i], uint32_t *,
424 				offsetof(struct ip, ip_src));
425 		if ((sa[i]->src != *src) || (sa[i]->dst != *(src + 1)))
426 			sa[i] = NULL;
427 	}
428 }
429 
430 void
431 outbound_sa_lookup(struct sa_ctx *sa_ctx, uint32_t sa_idx[],
432 		struct ipsec_sa *sa[], uint16_t nb_pkts)
433 {
434 	unsigned i;
435 
436 	for (i = 0; i < nb_pkts; i++)
437 		sa[i] = &sa_ctx->sa[sa_idx[i]];
438 }
439