09bfb865872fd07bbbdc82c1aecc39c3de73933b
[dpdk.git] / lib / librte_ipsec / esp_outb.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2018 Intel Corporation
3  */
4
5 #include <rte_ipsec.h>
6 #include <rte_esp.h>
7 #include <rte_ip.h>
8 #include <rte_errno.h>
9 #include <rte_cryptodev.h>
10
11 #include "sa.h"
12 #include "ipsec_sqn.h"
13 #include "crypto.h"
14 #include "iph.h"
15 #include "misc.h"
16 #include "pad.h"
17
18 /*
19  * setup crypto op and crypto sym op for ESP outbound packet.
20  */
21 static inline void
22 outb_cop_prepare(struct rte_crypto_op *cop,
23         const struct rte_ipsec_sa *sa, const uint64_t ivp[IPSEC_MAX_IV_QWORD],
24         const union sym_op_data *icv, uint32_t hlen, uint32_t plen)
25 {
26         struct rte_crypto_sym_op *sop;
27         struct aead_gcm_iv *gcm;
28         struct aesctr_cnt_blk *ctr;
29         uint32_t algo;
30
31         algo = sa->algo_type;
32
33         /* fill sym op fields */
34         sop = cop->sym;
35
36         switch (algo) {
37         case ALGO_TYPE_AES_CBC:
38                 /* Cipher-Auth (AES-CBC *) case */
39         case ALGO_TYPE_3DES_CBC:
40                 /* Cipher-Auth (3DES-CBC *) case */
41         case ALGO_TYPE_NULL:
42                 /* NULL case */
43                 sop->cipher.data.offset = sa->ctp.cipher.offset + hlen;
44                 sop->cipher.data.length = sa->ctp.cipher.length + plen;
45                 sop->auth.data.offset = sa->ctp.auth.offset + hlen;
46                 sop->auth.data.length = sa->ctp.auth.length + plen;
47                 sop->auth.digest.data = icv->va;
48                 sop->auth.digest.phys_addr = icv->pa;
49                 break;
50         case ALGO_TYPE_AES_GCM:
51                 /* AEAD (AES_GCM) case */
52                 sop->aead.data.offset = sa->ctp.cipher.offset + hlen;
53                 sop->aead.data.length = sa->ctp.cipher.length + plen;
54                 sop->aead.digest.data = icv->va;
55                 sop->aead.digest.phys_addr = icv->pa;
56                 sop->aead.aad.data = icv->va + sa->icv_len;
57                 sop->aead.aad.phys_addr = icv->pa + sa->icv_len;
58
59                 /* fill AAD IV (located inside crypto op) */
60                 gcm = rte_crypto_op_ctod_offset(cop, struct aead_gcm_iv *,
61                         sa->iv_ofs);
62                 aead_gcm_iv_fill(gcm, ivp[0], sa->salt);
63                 break;
64         case ALGO_TYPE_AES_CTR:
65                 /* Cipher-Auth (AES-CTR *) case */
66                 sop->cipher.data.offset = sa->ctp.cipher.offset + hlen;
67                 sop->cipher.data.length = sa->ctp.cipher.length + plen;
68                 sop->auth.data.offset = sa->ctp.auth.offset + hlen;
69                 sop->auth.data.length = sa->ctp.auth.length + plen;
70                 sop->auth.digest.data = icv->va;
71                 sop->auth.digest.phys_addr = icv->pa;
72
73                 ctr = rte_crypto_op_ctod_offset(cop, struct aesctr_cnt_blk *,
74                         sa->iv_ofs);
75                 aes_ctr_cnt_blk_fill(ctr, ivp[0], sa->salt);
76                 break;
77         }
78 }
79
80 /*
81  * setup/update packet data and metadata for ESP outbound tunnel case.
82  */
83 static inline int32_t
84 outb_tun_pkt_prepare(struct rte_ipsec_sa *sa, rte_be64_t sqc,
85         const uint64_t ivp[IPSEC_MAX_IV_QWORD], struct rte_mbuf *mb,
86         union sym_op_data *icv)
87 {
88         uint32_t clen, hlen, l2len, pdlen, pdofs, plen, tlen;
89         struct rte_mbuf *ml;
90         struct esp_hdr *esph;
91         struct esp_tail *espt;
92         char *ph, *pt;
93         uint64_t *iv;
94
95         /* calculate extra header space required */
96         hlen = sa->hdr_len + sa->iv_len + sizeof(*esph);
97
98         /* size of ipsec protected data */
99         l2len = mb->l2_len;
100         plen = mb->pkt_len - l2len;
101
102         /* number of bytes to encrypt */
103         clen = plen + sizeof(*espt);
104         clen = RTE_ALIGN_CEIL(clen, sa->pad_align);
105
106         /* pad length + esp tail */
107         pdlen = clen - plen;
108         tlen = pdlen + sa->icv_len;
109
110         /* do append and prepend */
111         ml = rte_pktmbuf_lastseg(mb);
112         if (tlen + sa->sqh_len + sa->aad_len > rte_pktmbuf_tailroom(ml))
113                 return -ENOSPC;
114
115         /* prepend header */
116         ph = rte_pktmbuf_prepend(mb, hlen - l2len);
117         if (ph == NULL)
118                 return -ENOSPC;
119
120         /* append tail */
121         pdofs = ml->data_len;
122         ml->data_len += tlen;
123         mb->pkt_len += tlen;
124         pt = rte_pktmbuf_mtod_offset(ml, typeof(pt), pdofs);
125
126         /* update pkt l2/l3 len */
127         mb->tx_offload = (mb->tx_offload & sa->tx_offload.msk) |
128                 sa->tx_offload.val;
129
130         /* copy tunnel pkt header */
131         rte_memcpy(ph, sa->hdr, sa->hdr_len);
132
133         /* update original and new ip header fields */
134         update_tun_l3hdr(sa, ph + sa->hdr_l3_off, mb->pkt_len, sa->hdr_l3_off,
135                         sqn_low16(sqc));
136
137         /* update spi, seqn and iv */
138         esph = (struct esp_hdr *)(ph + sa->hdr_len);
139         iv = (uint64_t *)(esph + 1);
140         copy_iv(iv, ivp, sa->iv_len);
141
142         esph->spi = sa->spi;
143         esph->seq = sqn_low32(sqc);
144
145         /* offset for ICV */
146         pdofs += pdlen + sa->sqh_len;
147
148         /* pad length */
149         pdlen -= sizeof(*espt);
150
151         /* copy padding data */
152         rte_memcpy(pt, esp_pad_bytes, pdlen);
153
154         /* update esp trailer */
155         espt = (struct esp_tail *)(pt + pdlen);
156         espt->pad_len = pdlen;
157         espt->next_proto = sa->proto;
158
159         icv->va = rte_pktmbuf_mtod_offset(ml, void *, pdofs);
160         icv->pa = rte_pktmbuf_iova_offset(ml, pdofs);
161
162         return clen;
163 }
164
165 /*
166  * for pure cryptodev (lookaside none) depending on SA settings,
167  * we might have to write some extra data to the packet.
168  */
169 static inline void
170 outb_pkt_xprepare(const struct rte_ipsec_sa *sa, rte_be64_t sqc,
171         const union sym_op_data *icv)
172 {
173         uint32_t *psqh;
174         struct aead_gcm_aad *aad;
175
176         /* insert SQN.hi between ESP trailer and ICV */
177         if (sa->sqh_len != 0) {
178                 psqh = (uint32_t *)(icv->va - sa->sqh_len);
179                 psqh[0] = sqn_hi32(sqc);
180         }
181
182         /*
183          * fill IV and AAD fields, if any (aad fields are placed after icv),
184          * right now we support only one AEAD algorithm: AES-GCM .
185          */
186         if (sa->aad_len != 0) {
187                 aad = (struct aead_gcm_aad *)(icv->va + sa->icv_len);
188                 aead_gcm_aad_fill(aad, sa->spi, sqc, IS_ESN(sa));
189         }
190 }
191
192 /*
193  * setup/update packets and crypto ops for ESP outbound tunnel case.
194  */
195 uint16_t
196 esp_outb_tun_prepare(const struct rte_ipsec_session *ss, struct rte_mbuf *mb[],
197         struct rte_crypto_op *cop[], uint16_t num)
198 {
199         int32_t rc;
200         uint32_t i, k, n;
201         uint64_t sqn;
202         rte_be64_t sqc;
203         struct rte_ipsec_sa *sa;
204         struct rte_cryptodev_sym_session *cs;
205         union sym_op_data icv;
206         uint64_t iv[IPSEC_MAX_IV_QWORD];
207         uint32_t dr[num];
208
209         sa = ss->sa;
210         cs = ss->crypto.ses;
211
212         n = num;
213         sqn = esn_outb_update_sqn(sa, &n);
214         if (n != num)
215                 rte_errno = EOVERFLOW;
216
217         k = 0;
218         for (i = 0; i != n; i++) {
219
220                 sqc = rte_cpu_to_be_64(sqn + i);
221                 gen_iv(iv, sqc);
222
223                 /* try to update the packet itself */
224                 rc = outb_tun_pkt_prepare(sa, sqc, iv, mb[i], &icv);
225
226                 /* success, setup crypto op */
227                 if (rc >= 0) {
228                         outb_pkt_xprepare(sa, sqc, &icv);
229                         lksd_none_cop_prepare(cop[k], cs, mb[i]);
230                         outb_cop_prepare(cop[k], sa, iv, &icv, 0, rc);
231                         k++;
232                 /* failure, put packet into the death-row */
233                 } else {
234                         dr[i - k] = i;
235                         rte_errno = -rc;
236                 }
237         }
238
239          /* copy not prepared mbufs beyond good ones */
240         if (k != n && k != 0)
241                 move_bad_mbufs(mb, dr, n, n - k);
242
243         return k;
244 }
245
246 /*
247  * setup/update packet data and metadata for ESP outbound transport case.
248  */
249 static inline int32_t
250 outb_trs_pkt_prepare(struct rte_ipsec_sa *sa, rte_be64_t sqc,
251         const uint64_t ivp[IPSEC_MAX_IV_QWORD], struct rte_mbuf *mb,
252         uint32_t l2len, uint32_t l3len, union sym_op_data *icv)
253 {
254         uint8_t np;
255         uint32_t clen, hlen, pdlen, pdofs, plen, tlen, uhlen;
256         struct rte_mbuf *ml;
257         struct esp_hdr *esph;
258         struct esp_tail *espt;
259         char *ph, *pt;
260         uint64_t *iv;
261
262         uhlen = l2len + l3len;
263         plen = mb->pkt_len - uhlen;
264
265         /* calculate extra header space required */
266         hlen = sa->iv_len + sizeof(*esph);
267
268         /* number of bytes to encrypt */
269         clen = plen + sizeof(*espt);
270         clen = RTE_ALIGN_CEIL(clen, sa->pad_align);
271
272         /* pad length + esp tail */
273         pdlen = clen - plen;
274         tlen = pdlen + sa->icv_len;
275
276         /* do append and insert */
277         ml = rte_pktmbuf_lastseg(mb);
278         if (tlen + sa->sqh_len + sa->aad_len > rte_pktmbuf_tailroom(ml))
279                 return -ENOSPC;
280
281         /* prepend space for ESP header */
282         ph = rte_pktmbuf_prepend(mb, hlen);
283         if (ph == NULL)
284                 return -ENOSPC;
285
286         /* append tail */
287         pdofs = ml->data_len;
288         ml->data_len += tlen;
289         mb->pkt_len += tlen;
290         pt = rte_pktmbuf_mtod_offset(ml, typeof(pt), pdofs);
291
292         /* shift L2/L3 headers */
293         insert_esph(ph, ph + hlen, uhlen);
294
295         /* update ip  header fields */
296         np = update_trs_l3hdr(sa, ph + l2len, mb->pkt_len, l2len, l3len,
297                         IPPROTO_ESP);
298
299         /* update spi, seqn and iv */
300         esph = (struct esp_hdr *)(ph + uhlen);
301         iv = (uint64_t *)(esph + 1);
302         copy_iv(iv, ivp, sa->iv_len);
303
304         esph->spi = sa->spi;
305         esph->seq = sqn_low32(sqc);
306
307         /* offset for ICV */
308         pdofs += pdlen + sa->sqh_len;
309
310         /* pad length */
311         pdlen -= sizeof(*espt);
312
313         /* copy padding data */
314         rte_memcpy(pt, esp_pad_bytes, pdlen);
315
316         /* update esp trailer */
317         espt = (struct esp_tail *)(pt + pdlen);
318         espt->pad_len = pdlen;
319         espt->next_proto = np;
320
321         icv->va = rte_pktmbuf_mtod_offset(ml, void *, pdofs);
322         icv->pa = rte_pktmbuf_iova_offset(ml, pdofs);
323
324         return clen;
325 }
326
327 /*
328  * setup/update packets and crypto ops for ESP outbound transport case.
329  */
330 uint16_t
331 esp_outb_trs_prepare(const struct rte_ipsec_session *ss, struct rte_mbuf *mb[],
332         struct rte_crypto_op *cop[], uint16_t num)
333 {
334         int32_t rc;
335         uint32_t i, k, n, l2, l3;
336         uint64_t sqn;
337         rte_be64_t sqc;
338         struct rte_ipsec_sa *sa;
339         struct rte_cryptodev_sym_session *cs;
340         union sym_op_data icv;
341         uint64_t iv[IPSEC_MAX_IV_QWORD];
342         uint32_t dr[num];
343
344         sa = ss->sa;
345         cs = ss->crypto.ses;
346
347         n = num;
348         sqn = esn_outb_update_sqn(sa, &n);
349         if (n != num)
350                 rte_errno = EOVERFLOW;
351
352         k = 0;
353         for (i = 0; i != n; i++) {
354
355                 l2 = mb[i]->l2_len;
356                 l3 = mb[i]->l3_len;
357
358                 sqc = rte_cpu_to_be_64(sqn + i);
359                 gen_iv(iv, sqc);
360
361                 /* try to update the packet itself */
362                 rc = outb_trs_pkt_prepare(sa, sqc, iv, mb[i], l2, l3, &icv);
363
364                 /* success, setup crypto op */
365                 if (rc >= 0) {
366                         outb_pkt_xprepare(sa, sqc, &icv);
367                         lksd_none_cop_prepare(cop[k], cs, mb[i]);
368                         outb_cop_prepare(cop[k], sa, iv, &icv, l2 + l3, rc);
369                         k++;
370                 /* failure, put packet into the death-row */
371                 } else {
372                         dr[i - k] = i;
373                         rte_errno = -rc;
374                 }
375         }
376
377         /* copy not prepared mbufs beyond good ones */
378         if (k != n && k != 0)
379                 move_bad_mbufs(mb, dr, n, n - k);
380
381         return k;
382 }
383
384 /*
385  * process outbound packets for SA with ESN support,
386  * for algorithms that require SQN.hibits to be implictly included
387  * into digest computation.
388  * In that case we have to move ICV bytes back to their proper place.
389  */
390 uint16_t
391 esp_outb_sqh_process(const struct rte_ipsec_session *ss, struct rte_mbuf *mb[],
392         uint16_t num)
393 {
394         uint32_t i, k, icv_len, *icv;
395         struct rte_mbuf *ml;
396         struct rte_ipsec_sa *sa;
397         uint32_t dr[num];
398
399         sa = ss->sa;
400
401         k = 0;
402         icv_len = sa->icv_len;
403
404         for (i = 0; i != num; i++) {
405                 if ((mb[i]->ol_flags & PKT_RX_SEC_OFFLOAD_FAILED) == 0) {
406                         ml = rte_pktmbuf_lastseg(mb[i]);
407                         icv = rte_pktmbuf_mtod_offset(ml, void *,
408                                 ml->data_len - icv_len);
409                         remove_sqh(icv, icv_len);
410                         k++;
411                 } else
412                         dr[i - k] = i;
413         }
414
415         /* handle unprocessed mbufs */
416         if (k != num) {
417                 rte_errno = EBADMSG;
418                 if (k != 0)
419                         move_bad_mbufs(mb, dr, num, num - k);
420         }
421
422         return k;
423 }
424
425 /*
426  * prepare packets for inline ipsec processing:
427  * set ol_flags and attach metadata.
428  */
429 static inline void
430 inline_outb_mbuf_prepare(const struct rte_ipsec_session *ss,
431         struct rte_mbuf *mb[], uint16_t num)
432 {
433         uint32_t i, ol_flags;
434
435         ol_flags = ss->security.ol_flags & RTE_SECURITY_TX_OLOAD_NEED_MDATA;
436         for (i = 0; i != num; i++) {
437
438                 mb[i]->ol_flags |= PKT_TX_SEC_OFFLOAD;
439                 if (ol_flags != 0)
440                         rte_security_set_pkt_metadata(ss->security.ctx,
441                                 ss->security.ses, mb[i], NULL);
442         }
443 }
444
445 /*
446  * process group of ESP outbound tunnel packets destined for
447  * INLINE_CRYPTO type of device.
448  */
449 uint16_t
450 inline_outb_tun_pkt_process(const struct rte_ipsec_session *ss,
451         struct rte_mbuf *mb[], uint16_t num)
452 {
453         int32_t rc;
454         uint32_t i, k, n;
455         uint64_t sqn;
456         rte_be64_t sqc;
457         struct rte_ipsec_sa *sa;
458         union sym_op_data icv;
459         uint64_t iv[IPSEC_MAX_IV_QWORD];
460         uint32_t dr[num];
461
462         sa = ss->sa;
463
464         n = num;
465         sqn = esn_outb_update_sqn(sa, &n);
466         if (n != num)
467                 rte_errno = EOVERFLOW;
468
469         k = 0;
470         for (i = 0; i != n; i++) {
471
472                 sqc = rte_cpu_to_be_64(sqn + i);
473                 gen_iv(iv, sqc);
474
475                 /* try to update the packet itself */
476                 rc = outb_tun_pkt_prepare(sa, sqc, iv, mb[i], &icv);
477
478                 k += (rc >= 0);
479
480                 /* failure, put packet into the death-row */
481                 if (rc < 0) {
482                         dr[i - k] = i;
483                         rte_errno = -rc;
484                 }
485         }
486
487         /* copy not processed mbufs beyond good ones */
488         if (k != n && k != 0)
489                 move_bad_mbufs(mb, dr, n, n - k);
490
491         inline_outb_mbuf_prepare(ss, mb, k);
492         return k;
493 }
494
495 /*
496  * process group of ESP outbound transport packets destined for
497  * INLINE_CRYPTO type of device.
498  */
499 uint16_t
500 inline_outb_trs_pkt_process(const struct rte_ipsec_session *ss,
501         struct rte_mbuf *mb[], uint16_t num)
502 {
503         int32_t rc;
504         uint32_t i, k, n, l2, l3;
505         uint64_t sqn;
506         rte_be64_t sqc;
507         struct rte_ipsec_sa *sa;
508         union sym_op_data icv;
509         uint64_t iv[IPSEC_MAX_IV_QWORD];
510         uint32_t dr[num];
511
512         sa = ss->sa;
513
514         n = num;
515         sqn = esn_outb_update_sqn(sa, &n);
516         if (n != num)
517                 rte_errno = EOVERFLOW;
518
519         k = 0;
520         for (i = 0; i != n; i++) {
521
522                 l2 = mb[i]->l2_len;
523                 l3 = mb[i]->l3_len;
524
525                 sqc = rte_cpu_to_be_64(sqn + i);
526                 gen_iv(iv, sqc);
527
528                 /* try to update the packet itself */
529                 rc = outb_trs_pkt_prepare(sa, sqc, iv, mb[i],
530                                 l2, l3, &icv);
531
532                 k += (rc >= 0);
533
534                 /* failure, put packet into the death-row */
535                 if (rc < 0) {
536                         dr[i - k] = i;
537                         rte_errno = -rc;
538                 }
539         }
540
541         /* copy not processed mbufs beyond good ones */
542         if (k != n && k != 0)
543                 move_bad_mbufs(mb, dr, n, n - k);
544
545         inline_outb_mbuf_prepare(ss, mb, k);
546         return k;
547 }
548
549 /*
550  * outbound for RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL:
551  * actual processing is done by HW/PMD, just set flags and metadata.
552  */
553 uint16_t
554 inline_proto_outb_pkt_process(const struct rte_ipsec_session *ss,
555         struct rte_mbuf *mb[], uint16_t num)
556 {
557         inline_outb_mbuf_prepare(ss, mb, num);
558         return num;
559 }