examples/ipsec-secgw: support IPv6
[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 <sys/types.h>
38 #include <netinet/in.h>
39 #include <netinet/ip.h>
40 #include <netinet/ip6.h>
41
42 #include <rte_memzone.h>
43 #include <rte_crypto.h>
44 #include <rte_cryptodev.h>
45 #include <rte_byteorder.h>
46 #include <rte_errno.h>
47 #include <rte_ip.h>
48
49 #include "ipsec.h"
50 #include "esp.h"
51
52 /* SAs Outbound */
53 const struct ipsec_sa sa_out[] = {
54         {
55         .spi = 5,
56         .src.ip4 = IPv4(172, 16, 1, 5),
57         .dst.ip4 = IPv4(172, 16, 2, 5),
58         .cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
59         .auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
60         .digest_len = 12,
61         .iv_len = 16,
62         .block_size = 16,
63         .flags = IP4_TUNNEL
64         },
65         {
66         .spi = 6,
67         .src.ip4 = IPv4(172, 16, 1, 6),
68         .dst.ip4 = IPv4(172, 16, 2, 6),
69         .cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
70         .auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
71         .digest_len = 12,
72         .iv_len = 16,
73         .block_size = 16,
74         .flags = IP4_TUNNEL
75         },
76         {
77         .spi = 15,
78         .src.ip4 = IPv4(172, 16, 1, 5),
79         .dst.ip4 = IPv4(172, 16, 2, 5),
80         .cipher_algo = RTE_CRYPTO_CIPHER_NULL,
81         .auth_algo = RTE_CRYPTO_AUTH_NULL,
82         .digest_len = 0,
83         .iv_len = 0,
84         .block_size = 4,
85         .flags = IP4_TUNNEL
86         },
87         {
88         .spi = 16,
89         .src.ip4 = IPv4(172, 16, 1, 6),
90         .dst.ip4 = IPv4(172, 16, 2, 6),
91         .cipher_algo = RTE_CRYPTO_CIPHER_NULL,
92         .auth_algo = RTE_CRYPTO_AUTH_NULL,
93         .digest_len = 0,
94         .iv_len = 0,
95         .block_size = 4,
96         .flags = IP4_TUNNEL
97         },
98         {
99         .spi = 25,
100         .src.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
101                 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55 },
102         .dst.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
103                 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x55, 0x55 },
104         .cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
105         .auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
106         .digest_len = 12,
107         .iv_len = 16,
108         .block_size = 16,
109         .flags = IP6_TUNNEL
110         },
111         {
112         .spi = 26,
113         .src.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
114                 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x66, 0x66 },
115         .dst.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
116                 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x66, 0x66 },
117         .cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
118         .auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
119         .digest_len = 12,
120         .iv_len = 16,
121         .block_size = 16,
122         .flags = IP6_TUNNEL
123         },
124 };
125
126 /* SAs Inbound */
127 const struct ipsec_sa sa_in[] = {
128         {
129         .spi = 105,
130         .src.ip4 = IPv4(172, 16, 2, 5),
131         .dst.ip4 = IPv4(172, 16, 1, 5),
132         .cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
133         .auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
134         .digest_len = 12,
135         .iv_len = 16,
136         .block_size = 16,
137         .flags = IP4_TUNNEL
138         },
139         {
140         .spi = 106,
141         .src.ip4 = IPv4(172, 16, 2, 6),
142         .dst.ip4 = IPv4(172, 16, 1, 6),
143         .cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
144         .auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
145         .digest_len = 12,
146         .iv_len = 16,
147         .block_size = 16,
148         .flags = IP4_TUNNEL
149         },
150         {
151         .spi = 115,
152         .src.ip4 = IPv4(172, 16, 2, 5),
153         .dst.ip4 = IPv4(172, 16, 1, 5),
154         .cipher_algo = RTE_CRYPTO_CIPHER_NULL,
155         .auth_algo = RTE_CRYPTO_AUTH_NULL,
156         .digest_len = 0,
157         .iv_len = 0,
158         .block_size = 4,
159         .flags = IP4_TUNNEL
160         },
161         {
162         .spi = 116,
163         .src.ip4 = IPv4(172, 16, 2, 6),
164         .dst.ip4 = IPv4(172, 16, 1, 6),
165         .cipher_algo = RTE_CRYPTO_CIPHER_NULL,
166         .auth_algo = RTE_CRYPTO_AUTH_NULL,
167         .digest_len = 0,
168         .iv_len = 0,
169         .block_size = 4,
170         .flags = IP4_TUNNEL
171         },
172         {
173         .spi = 125,
174         .src.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
175                 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x55, 0x55 },
176         .dst.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
177                 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x55, 0x55 },
178         .cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
179         .auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
180         .digest_len = 12,
181         .iv_len = 16,
182         .block_size = 16,
183         .flags = IP6_TUNNEL
184         },
185         {
186         .spi = 126,
187         .src.ip6_b = { 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
188                 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x66, 0x66 },
189         .dst.ip6_b = { 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
190                 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x66, 0x66 },
191         .cipher_algo = RTE_CRYPTO_CIPHER_AES_CBC,
192         .auth_algo = RTE_CRYPTO_AUTH_SHA1_HMAC,
193         .digest_len = 12,
194         .iv_len = 16,
195         .block_size = 16,
196         .flags = IP6_TUNNEL
197         },
198 };
199
200 static uint8_t cipher_key[256] = "sixteenbytes key";
201
202 /* AES CBC xform */
203 const struct rte_crypto_sym_xform aescbc_enc_xf = {
204         NULL,
205         RTE_CRYPTO_SYM_XFORM_CIPHER,
206         {.cipher = { RTE_CRYPTO_CIPHER_OP_ENCRYPT, RTE_CRYPTO_CIPHER_AES_CBC,
207                 .key = { cipher_key, 16 } }
208         }
209 };
210
211 const struct rte_crypto_sym_xform aescbc_dec_xf = {
212         NULL,
213         RTE_CRYPTO_SYM_XFORM_CIPHER,
214         {.cipher = { RTE_CRYPTO_CIPHER_OP_DECRYPT, RTE_CRYPTO_CIPHER_AES_CBC,
215                 .key = { cipher_key, 16 } }
216         }
217 };
218
219 static uint8_t auth_key[256] = "twentybytes hash key";
220
221 /* SHA1 HMAC xform */
222 const struct rte_crypto_sym_xform sha1hmac_gen_xf = {
223         NULL,
224         RTE_CRYPTO_SYM_XFORM_AUTH,
225         {.auth = { RTE_CRYPTO_AUTH_OP_GENERATE, RTE_CRYPTO_AUTH_SHA1_HMAC,
226                 .key = { auth_key, 20 }, 12, 0 }
227         }
228 };
229
230 const struct rte_crypto_sym_xform sha1hmac_verify_xf = {
231         NULL,
232         RTE_CRYPTO_SYM_XFORM_AUTH,
233         {.auth = { RTE_CRYPTO_AUTH_OP_VERIFY, RTE_CRYPTO_AUTH_SHA1_HMAC,
234                 .key = { auth_key, 20 }, 12, 0 }
235         }
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
246 const struct rte_crypto_sym_xform null_auth_xf = {
247         NULL,
248         RTE_CRYPTO_SYM_XFORM_AUTH,
249         {.auth = { .algo = RTE_CRYPTO_AUTH_NULL }
250         }
251 };
252
253 struct sa_ctx {
254         struct ipsec_sa sa[IPSEC_SA_MAX_ENTRIES];
255         struct {
256                 struct rte_crypto_sym_xform a;
257                 struct rte_crypto_sym_xform b;
258         } xf[IPSEC_SA_MAX_ENTRIES];
259 };
260
261 static struct sa_ctx *
262 sa_create(const char *name, int32_t socket_id)
263 {
264         char s[PATH_MAX];
265         struct sa_ctx *sa_ctx;
266         uint32_t mz_size;
267         const struct rte_memzone *mz;
268
269         snprintf(s, sizeof(s), "%s_%u", name, socket_id);
270
271         /* Create SA array table */
272         printf("Creating SA context with %u maximum entries\n",
273                         IPSEC_SA_MAX_ENTRIES);
274
275         mz_size = sizeof(struct sa_ctx);
276         mz = rte_memzone_reserve(s, mz_size, socket_id,
277                         RTE_MEMZONE_1GB | RTE_MEMZONE_SIZE_HINT_ONLY);
278         if (mz == NULL) {
279                 printf("Failed to allocate SA DB memory\n");
280                 rte_errno = -ENOMEM;
281                 return NULL;
282         }
283
284         sa_ctx = (struct sa_ctx *)mz->addr;
285
286         return sa_ctx;
287 }
288
289 static int
290 sa_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
291                 uint32_t nb_entries, uint32_t inbound)
292 {
293         struct ipsec_sa *sa;
294         uint32_t i, idx;
295
296         for (i = 0; i < nb_entries; i++) {
297                 idx = SPI2IDX(entries[i].spi);
298                 sa = &sa_ctx->sa[idx];
299                 if (sa->spi != 0) {
300                         printf("Index %u already in use by SPI %u\n",
301                                         idx, sa->spi);
302                         return -EINVAL;
303                 }
304                 *sa = entries[i];
305                 sa->seq = 0;
306
307                 switch (sa->flags) {
308                 case IP4_TUNNEL:
309                         sa->src.ip4 = rte_cpu_to_be_32(sa->src.ip4);
310                         sa->dst.ip4 = rte_cpu_to_be_32(sa->dst.ip4);
311                 }
312
313                 if (inbound) {
314                         if (sa->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
315                                 sa_ctx->xf[idx].a = null_auth_xf;
316                                 sa_ctx->xf[idx].b = null_cipher_xf;
317                         } else {
318                                 sa_ctx->xf[idx].a = sha1hmac_verify_xf;
319                                 sa_ctx->xf[idx].b = aescbc_dec_xf;
320                         }
321                 } else { /* outbound */
322                         if (sa->cipher_algo == RTE_CRYPTO_CIPHER_NULL) {
323                                 sa_ctx->xf[idx].a = null_cipher_xf;
324                                 sa_ctx->xf[idx].b = null_auth_xf;
325                         } else {
326                                 sa_ctx->xf[idx].a = aescbc_enc_xf;
327                                 sa_ctx->xf[idx].b = sha1hmac_gen_xf;
328                         }
329                 }
330                 sa_ctx->xf[idx].a.next = &sa_ctx->xf[idx].b;
331                 sa_ctx->xf[idx].b.next = NULL;
332                 sa->xforms = &sa_ctx->xf[idx].a;
333         }
334
335         return 0;
336 }
337
338 static inline int
339 sa_out_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
340                 uint32_t nb_entries)
341 {
342         return sa_add_rules(sa_ctx, entries, nb_entries, 0);
343 }
344
345 static inline int
346 sa_in_add_rules(struct sa_ctx *sa_ctx, const struct ipsec_sa entries[],
347                 uint32_t nb_entries)
348 {
349         return sa_add_rules(sa_ctx, entries, nb_entries, 1);
350 }
351
352 void
353 sa_init(struct socket_ctx *ctx, int32_t socket_id, uint32_t ep)
354 {
355         const struct ipsec_sa *sa_out_entries, *sa_in_entries;
356         uint32_t nb_out_entries, nb_in_entries;
357         const char *name;
358
359         if (ctx == NULL)
360                 rte_exit(EXIT_FAILURE, "NULL context.\n");
361
362         if (ctx->sa_in != NULL)
363                 rte_exit(EXIT_FAILURE, "Inbound SA DB for socket %u already "
364                                 "initialized\n", socket_id);
365
366         if (ctx->sa_out != NULL)
367                 rte_exit(EXIT_FAILURE, "Outbound SA DB for socket %u already "
368                                 "initialized\n", socket_id);
369
370         if (ep == 0) {
371                 sa_out_entries = sa_out;
372                 nb_out_entries = RTE_DIM(sa_out);
373                 sa_in_entries = sa_in;
374                 nb_in_entries = RTE_DIM(sa_in);
375         } else if (ep == 1) {
376                 sa_out_entries = sa_in;
377                 nb_out_entries = RTE_DIM(sa_in);
378                 sa_in_entries = sa_out;
379                 nb_in_entries = RTE_DIM(sa_out);
380         } else
381                 rte_exit(EXIT_FAILURE, "Invalid EP value %u. "
382                                 "Only 0 or 1 supported.\n", ep);
383
384         name = "sa_in";
385         ctx->sa_in = sa_create(name, socket_id);
386         if (ctx->sa_in == NULL)
387                 rte_exit(EXIT_FAILURE, "Error [%d] creating SA context %s "
388                                 "in socket %d\n", rte_errno, name, socket_id);
389
390         name = "sa_out";
391         ctx->sa_out = sa_create(name, socket_id);
392         if (ctx->sa_out == NULL)
393                 rte_exit(EXIT_FAILURE, "Error [%d] creating SA context %s "
394                                 "in socket %d\n", rte_errno, name, socket_id);
395
396         sa_in_add_rules(ctx->sa_in, sa_in_entries, nb_in_entries);
397
398         sa_out_add_rules(ctx->sa_out, sa_out_entries, nb_out_entries);
399 }
400
401 int
402 inbound_sa_check(struct sa_ctx *sa_ctx, struct rte_mbuf *m, uint32_t sa_idx)
403 {
404         struct ipsec_mbuf_metadata *priv;
405
406         priv = RTE_PTR_ADD(m, sizeof(struct rte_mbuf));
407
408         return (sa_ctx->sa[sa_idx].spi == priv->sa->spi);
409 }
410
411 static inline void
412 single_inbound_lookup(struct ipsec_sa *sadb, struct rte_mbuf *pkt,
413                 struct ipsec_sa **sa_ret)
414 {
415         struct esp_hdr *esp;
416         struct ip *ip;
417         uint32_t *src4_addr;
418         uint8_t *src6_addr;
419         struct ipsec_sa *sa;
420
421         *sa_ret = NULL;
422
423         ip = rte_pktmbuf_mtod(pkt, struct ip *);
424         if (ip->ip_v == IPVERSION)
425                 esp = (struct esp_hdr *)(ip + 1);
426         else
427                 esp = (struct esp_hdr *)(((struct ip6_hdr *)ip) + 1);
428
429         if (esp->spi == INVALID_SPI)
430                 return;
431
432         sa = &sadb[SPI2IDX(rte_be_to_cpu_32(esp->spi))];
433         if (rte_be_to_cpu_32(esp->spi) != sa->spi)
434                 return;
435
436         switch (sa->flags) {
437         case IP4_TUNNEL:
438                 src4_addr = RTE_PTR_ADD(ip, offsetof(struct ip, ip_src));
439                 if ((ip->ip_v == IPVERSION) &&
440                                 (sa->src.ip4 == *src4_addr) &&
441                                 (sa->dst.ip4 == *(src4_addr + 1)))
442                         *sa_ret = sa;
443                 break;
444         case IP6_TUNNEL:
445                 src6_addr = RTE_PTR_ADD(ip, offsetof(struct ip6_hdr, ip6_src));
446                 if ((ip->ip_v == IP6_VERSION) &&
447                                 !memcmp(&sa->src.ip6, src6_addr, 16) &&
448                                 !memcmp(&sa->dst.ip6, src6_addr + 16, 16))
449                         *sa_ret = sa;
450         }
451 }
452
453 void
454 inbound_sa_lookup(struct sa_ctx *sa_ctx, struct rte_mbuf *pkts[],
455                 struct ipsec_sa *sa[], uint16_t nb_pkts)
456 {
457         uint32_t i;
458
459         for (i = 0; i < nb_pkts; i++)
460                 single_inbound_lookup(sa_ctx->sa, pkts[i], &sa[i]);
461 }
462
463 void
464 outbound_sa_lookup(struct sa_ctx *sa_ctx, uint32_t sa_idx[],
465                 struct ipsec_sa *sa[], uint16_t nb_pkts)
466 {
467         uint32_t i;
468
469         for (i = 0; i < nb_pkts; i++)
470                 sa[i] = &sa_ctx->sa[sa_idx[i]];
471 }