ipsec: move inbound and outbound code
[dpdk.git] / lib / librte_ipsec / esp_inb.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 inbound tunnel packet.
20  */
21 static inline int32_t
22 inb_cop_prepare(struct rte_crypto_op *cop,
23         const struct rte_ipsec_sa *sa, struct rte_mbuf *mb,
24         const union sym_op_data *icv, uint32_t pofs, uint32_t plen)
25 {
26         struct rte_crypto_sym_op *sop;
27         struct aead_gcm_iv *gcm;
28         struct aesctr_cnt_blk *ctr;
29         uint64_t *ivc, *ivp;
30         uint32_t algo, clen;
31
32         clen = plen - sa->ctp.cipher.length;
33         if ((int32_t)clen < 0 || (clen & (sa->pad_align - 1)) != 0)
34                 return -EINVAL;
35
36         algo = sa->algo_type;
37
38         /* fill sym op fields */
39         sop = cop->sym;
40
41         switch (algo) {
42         case ALGO_TYPE_AES_GCM:
43                 sop->aead.data.offset = pofs + sa->ctp.cipher.offset;
44                 sop->aead.data.length = clen;
45                 sop->aead.digest.data = icv->va;
46                 sop->aead.digest.phys_addr = icv->pa;
47                 sop->aead.aad.data = icv->va + sa->icv_len;
48                 sop->aead.aad.phys_addr = icv->pa + sa->icv_len;
49
50                 /* fill AAD IV (located inside crypto op) */
51                 gcm = rte_crypto_op_ctod_offset(cop, struct aead_gcm_iv *,
52                         sa->iv_ofs);
53                 ivp = rte_pktmbuf_mtod_offset(mb, uint64_t *,
54                         pofs + sizeof(struct esp_hdr));
55                 aead_gcm_iv_fill(gcm, ivp[0], sa->salt);
56                 break;
57         case ALGO_TYPE_AES_CBC:
58         case ALGO_TYPE_3DES_CBC:
59                 sop->cipher.data.offset = pofs + sa->ctp.cipher.offset;
60                 sop->cipher.data.length = clen;
61                 sop->auth.data.offset = pofs + sa->ctp.auth.offset;
62                 sop->auth.data.length = plen - sa->ctp.auth.length;
63                 sop->auth.digest.data = icv->va;
64                 sop->auth.digest.phys_addr = icv->pa;
65
66                 /* copy iv from the input packet to the cop */
67                 ivc = rte_crypto_op_ctod_offset(cop, uint64_t *, sa->iv_ofs);
68                 ivp = rte_pktmbuf_mtod_offset(mb, uint64_t *,
69                         pofs + sizeof(struct esp_hdr));
70                 copy_iv(ivc, ivp, sa->iv_len);
71                 break;
72         case ALGO_TYPE_AES_CTR:
73                 sop->cipher.data.offset = pofs + sa->ctp.cipher.offset;
74                 sop->cipher.data.length = clen;
75                 sop->auth.data.offset = pofs + sa->ctp.auth.offset;
76                 sop->auth.data.length = plen - sa->ctp.auth.length;
77                 sop->auth.digest.data = icv->va;
78                 sop->auth.digest.phys_addr = icv->pa;
79
80                 /* copy iv from the input packet to the cop */
81                 ctr = rte_crypto_op_ctod_offset(cop, struct aesctr_cnt_blk *,
82                         sa->iv_ofs);
83                 ivp = rte_pktmbuf_mtod_offset(mb, uint64_t *,
84                         pofs + sizeof(struct esp_hdr));
85                 aes_ctr_cnt_blk_fill(ctr, ivp[0], sa->salt);
86                 break;
87         case ALGO_TYPE_NULL:
88                 sop->cipher.data.offset = pofs + sa->ctp.cipher.offset;
89                 sop->cipher.data.length = clen;
90                 sop->auth.data.offset = pofs + sa->ctp.auth.offset;
91                 sop->auth.data.length = plen - sa->ctp.auth.length;
92                 sop->auth.digest.data = icv->va;
93                 sop->auth.digest.phys_addr = icv->pa;
94                 break;
95         default:
96                 return -EINVAL;
97         }
98
99         return 0;
100 }
101
102 /*
103  * for pure cryptodev (lookaside none) depending on SA settings,
104  * we might have to write some extra data to the packet.
105  */
106 static inline void
107 inb_pkt_xprepare(const struct rte_ipsec_sa *sa, rte_be64_t sqc,
108         const union sym_op_data *icv)
109 {
110         struct aead_gcm_aad *aad;
111
112         /* insert SQN.hi between ESP trailer and ICV */
113         if (sa->sqh_len != 0)
114                 insert_sqh(sqn_hi32(sqc), icv->va, sa->icv_len);
115
116         /*
117          * fill AAD fields, if any (aad fields are placed after icv),
118          * right now we support only one AEAD algorithm: AES-GCM.
119          */
120         if (sa->aad_len != 0) {
121                 aad = (struct aead_gcm_aad *)(icv->va + sa->icv_len);
122                 aead_gcm_aad_fill(aad, sa->spi, sqc, IS_ESN(sa));
123         }
124 }
125
126 /*
127  * setup/update packet data and metadata for ESP inbound tunnel case.
128  */
129 static inline int32_t
130 inb_pkt_prepare(const struct rte_ipsec_sa *sa, const struct replay_sqn *rsn,
131         struct rte_mbuf *mb, uint32_t hlen, union sym_op_data *icv)
132 {
133         int32_t rc;
134         uint64_t sqn;
135         uint32_t icv_ofs, plen;
136         struct rte_mbuf *ml;
137         struct esp_hdr *esph;
138
139         esph = rte_pktmbuf_mtod_offset(mb, struct esp_hdr *, hlen);
140
141         /*
142          * retrieve and reconstruct SQN, then check it, then
143          * convert it back into network byte order.
144          */
145         sqn = rte_be_to_cpu_32(esph->seq);
146         if (IS_ESN(sa))
147                 sqn = reconstruct_esn(rsn->sqn, sqn, sa->replay.win_sz);
148
149         rc = esn_inb_check_sqn(rsn, sa, sqn);
150         if (rc != 0)
151                 return rc;
152
153         sqn = rte_cpu_to_be_64(sqn);
154
155         /* start packet manipulation */
156         plen = mb->pkt_len;
157         plen = plen - hlen;
158
159         ml = rte_pktmbuf_lastseg(mb);
160         icv_ofs = ml->data_len - sa->icv_len + sa->sqh_len;
161
162         /* we have to allocate space for AAD somewhere,
163          * right now - just use free trailing space at the last segment.
164          * Would probably be more convenient to reserve space for AAD
165          * inside rte_crypto_op itself
166          * (again for IV space is already reserved inside cop).
167          */
168         if (sa->aad_len + sa->sqh_len > rte_pktmbuf_tailroom(ml))
169                 return -ENOSPC;
170
171         icv->va = rte_pktmbuf_mtod_offset(ml, void *, icv_ofs);
172         icv->pa = rte_pktmbuf_iova_offset(ml, icv_ofs);
173
174         inb_pkt_xprepare(sa, sqn, icv);
175         return plen;
176 }
177
178 /*
179  * setup/update packets and crypto ops for ESP inbound case.
180  */
181 uint16_t
182 esp_inb_pkt_prepare(const struct rte_ipsec_session *ss, struct rte_mbuf *mb[],
183         struct rte_crypto_op *cop[], uint16_t num)
184 {
185         int32_t rc;
186         uint32_t i, k, hl;
187         struct rte_ipsec_sa *sa;
188         struct rte_cryptodev_sym_session *cs;
189         struct replay_sqn *rsn;
190         union sym_op_data icv;
191         uint32_t dr[num];
192
193         sa = ss->sa;
194         cs = ss->crypto.ses;
195         rsn = rsn_acquire(sa);
196
197         k = 0;
198         for (i = 0; i != num; i++) {
199
200                 hl = mb[i]->l2_len + mb[i]->l3_len;
201                 rc = inb_pkt_prepare(sa, rsn, mb[i], hl, &icv);
202                 if (rc >= 0) {
203                         lksd_none_cop_prepare(cop[k], cs, mb[i]);
204                         rc = inb_cop_prepare(cop[k], sa, mb[i], &icv, hl, rc);
205                 }
206
207                 k += (rc == 0);
208                 if (rc != 0) {
209                         dr[i - k] = i;
210                         rte_errno = -rc;
211                 }
212         }
213
214         rsn_release(sa, rsn);
215
216         /* copy not prepared mbufs beyond good ones */
217         if (k != num && k != 0)
218                 move_bad_mbufs(mb, dr, num, num - k);
219
220         return k;
221 }
222
223 /*
224  * process ESP inbound tunnel packet.
225  */
226 static inline int
227 inb_tun_single_pkt_process(struct rte_ipsec_sa *sa, struct rte_mbuf *mb,
228         uint32_t *sqn)
229 {
230         uint32_t hlen, icv_len, tlen;
231         struct esp_hdr *esph;
232         struct esp_tail *espt;
233         struct rte_mbuf *ml;
234         char *pd;
235
236         if (mb->ol_flags & PKT_RX_SEC_OFFLOAD_FAILED)
237                 return -EBADMSG;
238
239         icv_len = sa->icv_len;
240
241         ml = rte_pktmbuf_lastseg(mb);
242         espt = rte_pktmbuf_mtod_offset(ml, struct esp_tail *,
243                 ml->data_len - icv_len - sizeof(*espt));
244
245         /*
246          * check padding and next proto.
247          * return an error if something is wrong.
248          */
249         pd = (char *)espt - espt->pad_len;
250         if (espt->next_proto != sa->proto ||
251                         memcmp(pd, esp_pad_bytes, espt->pad_len))
252                 return -EINVAL;
253
254         /* cut of ICV, ESP tail and padding bytes */
255         tlen = icv_len + sizeof(*espt) + espt->pad_len;
256         ml->data_len -= tlen;
257         mb->pkt_len -= tlen;
258
259         /* cut of L2/L3 headers, ESP header and IV */
260         hlen = mb->l2_len + mb->l3_len;
261         esph = rte_pktmbuf_mtod_offset(mb, struct esp_hdr *, hlen);
262         rte_pktmbuf_adj(mb, hlen + sa->ctp.cipher.offset);
263
264         /* retrieve SQN for later check */
265         *sqn = rte_be_to_cpu_32(esph->seq);
266
267         /* reset mbuf metatdata: L2/L3 len, packet type */
268         mb->packet_type = RTE_PTYPE_UNKNOWN;
269         mb->tx_offload = (mb->tx_offload & sa->tx_offload.msk) |
270                 sa->tx_offload.val;
271
272         /* clear the PKT_RX_SEC_OFFLOAD flag if set */
273         mb->ol_flags &= ~(mb->ol_flags & PKT_RX_SEC_OFFLOAD);
274         return 0;
275 }
276
277 /*
278  * process ESP inbound transport packet.
279  */
280 static inline int
281 inb_trs_single_pkt_process(struct rte_ipsec_sa *sa, struct rte_mbuf *mb,
282         uint32_t *sqn)
283 {
284         uint32_t hlen, icv_len, l2len, l3len, tlen;
285         struct esp_hdr *esph;
286         struct esp_tail *espt;
287         struct rte_mbuf *ml;
288         char *np, *op, *pd;
289
290         if (mb->ol_flags & PKT_RX_SEC_OFFLOAD_FAILED)
291                 return -EBADMSG;
292
293         icv_len = sa->icv_len;
294
295         ml = rte_pktmbuf_lastseg(mb);
296         espt = rte_pktmbuf_mtod_offset(ml, struct esp_tail *,
297                 ml->data_len - icv_len - sizeof(*espt));
298
299         /* check padding, return an error if something is wrong. */
300         pd = (char *)espt - espt->pad_len;
301         if (memcmp(pd, esp_pad_bytes, espt->pad_len))
302                 return -EINVAL;
303
304         /* cut of ICV, ESP tail and padding bytes */
305         tlen = icv_len + sizeof(*espt) + espt->pad_len;
306         ml->data_len -= tlen;
307         mb->pkt_len -= tlen;
308
309         /* retrieve SQN for later check */
310         l2len = mb->l2_len;
311         l3len = mb->l3_len;
312         hlen = l2len + l3len;
313         op = rte_pktmbuf_mtod(mb, char *);
314         esph = (struct esp_hdr *)(op + hlen);
315         *sqn = rte_be_to_cpu_32(esph->seq);
316
317         /* cut off ESP header and IV, update L3 header */
318         np = rte_pktmbuf_adj(mb, sa->ctp.cipher.offset);
319         remove_esph(np, op, hlen);
320         update_trs_l3hdr(sa, np + l2len, mb->pkt_len, l2len, l3len,
321                         espt->next_proto);
322
323         /* reset mbuf packet type */
324         mb->packet_type &= (RTE_PTYPE_L2_MASK | RTE_PTYPE_L3_MASK);
325
326         /* clear the PKT_RX_SEC_OFFLOAD flag if set */
327         mb->ol_flags &= ~(mb->ol_flags & PKT_RX_SEC_OFFLOAD);
328         return 0;
329 }
330
331 /*
332  * for group of ESP inbound packets perform SQN check and update.
333  */
334 static inline uint16_t
335 esp_inb_rsn_update(struct rte_ipsec_sa *sa, const uint32_t sqn[],
336         uint32_t dr[], uint16_t num)
337 {
338         uint32_t i, k;
339         struct replay_sqn *rsn;
340
341         rsn = rsn_update_start(sa);
342
343         k = 0;
344         for (i = 0; i != num; i++) {
345                 if (esn_inb_update_sqn(rsn, sa, sqn[i]) == 0)
346                         k++;
347                 else
348                         dr[i - k] = i;
349         }
350
351         rsn_update_finish(sa, rsn);
352         return k;
353 }
354
355 /*
356  * process group of ESP inbound tunnel packets.
357  */
358 uint16_t
359 esp_inb_tun_pkt_process(const struct rte_ipsec_session *ss,
360         struct rte_mbuf *mb[], uint16_t num)
361 {
362         uint32_t i, k, n;
363         struct rte_ipsec_sa *sa;
364         uint32_t sqn[num];
365         uint32_t dr[num];
366
367         sa = ss->sa;
368
369         /* process packets, extract seq numbers */
370
371         k = 0;
372         for (i = 0; i != num; i++) {
373                 /* good packet */
374                 if (inb_tun_single_pkt_process(sa, mb[i], sqn + k) == 0)
375                         k++;
376                 /* bad packet, will drop from furhter processing */
377                 else
378                         dr[i - k] = i;
379         }
380
381         /* handle unprocessed mbufs */
382         if (k != num && k != 0)
383                 move_bad_mbufs(mb, dr, num, num - k);
384
385         /* update SQN and replay winow */
386         n = esp_inb_rsn_update(sa, sqn, dr, k);
387
388         /* handle mbufs with wrong SQN */
389         if (n != k && n != 0)
390                 move_bad_mbufs(mb, dr, k, k - n);
391
392         if (n != num)
393                 rte_errno = EBADMSG;
394
395         return n;
396 }
397
398 /*
399  * process group of ESP inbound transport packets.
400  */
401 uint16_t
402 esp_inb_trs_pkt_process(const struct rte_ipsec_session *ss,
403         struct rte_mbuf *mb[], uint16_t num)
404 {
405         uint32_t i, k, n;
406         uint32_t sqn[num];
407         struct rte_ipsec_sa *sa;
408         uint32_t dr[num];
409
410         sa = ss->sa;
411
412         /* process packets, extract seq numbers */
413
414         k = 0;
415         for (i = 0; i != num; i++) {
416                 /* good packet */
417                 if (inb_trs_single_pkt_process(sa, mb[i], sqn + k) == 0)
418                         k++;
419                 /* bad packet, will drop from furhter processing */
420                 else
421                         dr[i - k] = i;
422         }
423
424         /* handle unprocessed mbufs */
425         if (k != num && k != 0)
426                 move_bad_mbufs(mb, dr, num, num - k);
427
428         /* update SQN and replay winow */
429         n = esp_inb_rsn_update(sa, sqn, dr, k);
430
431         /* handle mbufs with wrong SQN */
432         if (n != k && n != 0)
433                 move_bad_mbufs(mb, dr, k, k - n);
434
435         if (n != num)
436                 rte_errno = EBADMSG;
437
438         return n;
439 }