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