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