examples/ipsec-secgw: add IPsec sample application
[dpdk.git] / examples / ipsec-secgw / sa.c
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 }