+static_always_inline u32
+esp_encrypt_chain_crypto (vlib_main_t * vm, ipsec_per_thread_data_t * ptd,
+ ipsec_sa_t * sa0, vlib_buffer_t * b,
+ vlib_buffer_t * lb, u8 icv_sz, u8 * start,
+ u32 start_len, u16 * n_ch)
+{
+ vnet_crypto_op_chunk_t *ch;
+ vlib_buffer_t *cb = b;
+ u32 n_chunks = 1;
+ u32 total_len;
+ vec_add2 (ptd->chunks, ch, 1);
+ total_len = ch->len = start_len;
+ ch->src = ch->dst = start;
+ cb = vlib_get_buffer (vm, cb->next_buffer);
+
+ while (1)
+ {
+ vec_add2 (ptd->chunks, ch, 1);
+ n_chunks += 1;
+ if (lb == cb)
+ total_len += ch->len = cb->current_length - icv_sz;
+ else
+ total_len += ch->len = cb->current_length;
+ ch->src = ch->dst = vlib_buffer_get_current (cb);
+
+ if (!(cb->flags & VLIB_BUFFER_NEXT_PRESENT))
+ break;
+
+ cb = vlib_get_buffer (vm, cb->next_buffer);
+ }
+
+ if (n_ch)
+ *n_ch = n_chunks;
+
+ return total_len;
+}
+
+static_always_inline u32
+esp_encrypt_chain_integ (vlib_main_t * vm, ipsec_per_thread_data_t * ptd,
+ ipsec_sa_t * sa0, vlib_buffer_t * b,
+ vlib_buffer_t * lb, u8 icv_sz, u8 * start,
+ u32 start_len, u8 * digest, u16 * n_ch)
+{
+ vnet_crypto_op_chunk_t *ch;
+ vlib_buffer_t *cb = b;
+ u32 n_chunks = 1;
+ u32 total_len;
+ vec_add2 (ptd->chunks, ch, 1);
+ total_len = ch->len = start_len;
+ ch->src = start;
+ cb = vlib_get_buffer (vm, cb->next_buffer);
+
+ while (1)
+ {
+ vec_add2 (ptd->chunks, ch, 1);
+ n_chunks += 1;
+ if (lb == cb)
+ {
+ total_len += ch->len = cb->current_length - icv_sz;
+ if (ipsec_sa_is_set_USE_ESN (sa0))
+ {
+ u32 seq_hi = clib_net_to_host_u32 (sa0->seq_hi);
+ clib_memcpy_fast (digest, &seq_hi, sizeof (seq_hi));
+ ch->len += sizeof (seq_hi);
+ total_len += sizeof (seq_hi);
+ }
+ }
+ else
+ total_len += ch->len = cb->current_length;
+ ch->src = vlib_buffer_get_current (cb);
+
+ if (!(cb->flags & VLIB_BUFFER_NEXT_PRESENT))
+ break;
+
+ cb = vlib_get_buffer (vm, cb->next_buffer);
+ }
+
+ if (n_ch)
+ *n_ch = n_chunks;
+
+ return total_len;
+}
+
+always_inline void
+esp_prepare_sync_op (vlib_main_t * vm, ipsec_per_thread_data_t * ptd,
+ vnet_crypto_op_t ** crypto_ops,
+ vnet_crypto_op_t ** integ_ops, ipsec_sa_t * sa0,
+ u8 * payload, u16 payload_len, u8 iv_sz, u8 icv_sz,
+ vlib_buffer_t ** bufs, vlib_buffer_t ** b,
+ vlib_buffer_t * lb, u32 hdr_len, esp_header_t * esp,
+ esp_gcm_nonce_t * nonce)
+{
+ if (sa0->crypto_enc_op_id)
+ {
+ vnet_crypto_op_t *op;
+ vec_add2_aligned (crypto_ops[0], op, 1, CLIB_CACHE_LINE_BYTES);
+ vnet_crypto_op_init (op, sa0->crypto_enc_op_id);
+
+ op->src = op->dst = payload;
+ op->key_index = sa0->crypto_key_index;
+ op->len = payload_len - icv_sz;
+ op->user_data = b - bufs;
+
+ if (ipsec_sa_is_set_IS_AEAD (sa0))
+ {
+ /*
+ * construct the AAD in a scratch space in front
+ * of the IP header.
+ */
+ op->aad = payload - hdr_len - sizeof (esp_aead_t);
+ op->aad_len = esp_aad_fill (op->aad, esp, sa0);
+
+ op->tag = payload + op->len;
+ op->tag_len = 16;
+
+ u64 *iv = (u64 *) (payload - iv_sz);
+ nonce->salt = sa0->salt;
+ nonce->iv = *iv = clib_host_to_net_u64 (sa0->gcm_iv_counter++);
+ op->iv = (u8 *) nonce;
+ }
+ else
+ {
+ op->iv = payload - iv_sz;
+ op->flags = VNET_CRYPTO_OP_FLAG_INIT_IV;
+ }
+
+ if (lb != b[0])
+ {
+ /* is chained */
+ op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
+ op->chunk_index = vec_len (ptd->chunks);
+ op->tag = vlib_buffer_get_tail (lb) - icv_sz;
+ esp_encrypt_chain_crypto (vm, ptd, sa0, b[0], lb, icv_sz, payload,
+ payload_len, &op->n_chunks);
+ }
+ }
+
+ if (sa0->integ_op_id)
+ {
+ vnet_crypto_op_t *op;
+ vec_add2_aligned (integ_ops[0], op, 1, CLIB_CACHE_LINE_BYTES);
+ vnet_crypto_op_init (op, sa0->integ_op_id);
+ op->src = payload - iv_sz - sizeof (esp_header_t);
+ op->digest = payload + payload_len - icv_sz;
+ op->key_index = sa0->integ_key_index;
+ op->digest_len = icv_sz;
+ op->len = payload_len - icv_sz + iv_sz + sizeof (esp_header_t);
+ op->user_data = b - bufs;
+
+ if (lb != b[0])
+ {
+ /* is chained */
+ op->flags |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
+ op->chunk_index = vec_len (ptd->chunks);
+ op->digest = vlib_buffer_get_tail (lb) - icv_sz;
+
+ esp_encrypt_chain_integ (vm, ptd, sa0, b[0], lb, icv_sz,
+ payload - iv_sz - sizeof (esp_header_t),
+ payload_len + iv_sz +
+ sizeof (esp_header_t), op->digest,
+ &op->n_chunks);
+ }
+ else if (ipsec_sa_is_set_USE_ESN (sa0))
+ {
+ u32 seq_hi = clib_net_to_host_u32 (sa0->seq_hi);
+ clib_memcpy_fast (op->digest, &seq_hi, sizeof (seq_hi));
+ op->len += sizeof (seq_hi);
+ }
+ }
+}
+
+static_always_inline int
+esp_prepare_async_frame (vlib_main_t * vm, ipsec_per_thread_data_t * ptd,
+ vnet_crypto_async_frame_t ** async_frame,
+ ipsec_sa_t * sa, vlib_buffer_t * b,
+ esp_header_t * esp, u8 * payload, u32 payload_len,
+ u8 iv_sz, u8 icv_sz, u32 bi, u16 * next, u32 hdr_len,
+ u16 async_next, vlib_buffer_t * lb)
+{
+ esp_post_data_t *post = esp_post_data (b);
+ u8 *tag, *iv, *aad = 0;
+ u8 flag = 0;
+ u32 key_index;
+ i16 crypto_start_offset, integ_start_offset = 0;
+ u16 crypto_total_len, integ_total_len;
+
+ post->next_index = next[0];
+ next[0] = ESP_ENCRYPT_NEXT_PENDING;
+
+ /* crypto */
+ crypto_start_offset = payload - b->data;
+ crypto_total_len = integ_total_len = payload_len - icv_sz;
+ tag = payload + crypto_total_len;
+
+ /* aead */
+ if (ipsec_sa_is_set_IS_AEAD (sa))
+ {
+ esp_gcm_nonce_t *nonce;
+ u64 *pkt_iv = (u64 *) (payload - iv_sz);
+
+ aad = payload - hdr_len - sizeof (esp_aead_t);
+ esp_aad_fill (aad, esp, sa);
+ nonce = (esp_gcm_nonce_t *) (aad - sizeof (*nonce));
+ nonce->salt = sa->salt;
+ nonce->iv = *pkt_iv = clib_host_to_net_u64 (sa->gcm_iv_counter++);
+ iv = (u8 *) nonce;
+ key_index = sa->crypto_key_index;
+
+ if (lb != b)
+ {
+ /* chain */
+ flag |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
+ tag = vlib_buffer_get_tail (lb) - icv_sz;
+ crypto_total_len = esp_encrypt_chain_crypto (vm, ptd, sa, b, lb,
+ icv_sz, payload,
+ payload_len, 0);
+ }
+ goto out;
+ }
+
+ /* cipher then hash */
+ iv = payload - iv_sz;
+ integ_start_offset = crypto_start_offset - iv_sz - sizeof (esp_header_t);
+ integ_total_len += iv_sz + sizeof (esp_header_t);
+ flag |= VNET_CRYPTO_OP_FLAG_INIT_IV;
+ key_index = sa->linked_key_index;
+
+ if (b != lb)
+ {
+ flag |= VNET_CRYPTO_OP_FLAG_CHAINED_BUFFERS;
+ crypto_total_len = esp_encrypt_chain_crypto (vm, ptd, sa, b, lb,
+ icv_sz, payload,
+ payload_len, 0);
+ tag = vlib_buffer_get_tail (lb) - icv_sz;
+ integ_total_len = esp_encrypt_chain_integ (vm, ptd, sa, b, lb, icv_sz,
+ payload - iv_sz -
+ sizeof (esp_header_t),
+ payload_len + iv_sz +
+ sizeof (esp_header_t),
+ tag, 0);
+ }
+ else if (ipsec_sa_is_set_USE_ESN (sa) && !ipsec_sa_is_set_IS_AEAD (sa))
+ {
+ u32 seq_hi = clib_net_to_host_u32 (sa->seq_hi);
+ clib_memcpy_fast (tag, &seq_hi, sizeof (seq_hi));
+ integ_total_len += sizeof (seq_hi);
+ }
+
+out:
+ return vnet_crypto_async_add_to_frame (vm, async_frame, key_index,
+ crypto_total_len,
+ integ_total_len - crypto_total_len,
+ crypto_start_offset,
+ integ_start_offset, bi, async_next,
+ iv, tag, aad, flag);
+}
+
+/* when submitting a frame is failed, drop all buffers in the frame */
+static_always_inline void
+esp_async_recycle_failed_submit (vnet_crypto_async_frame_t * f,
+ vlib_buffer_t ** b, u16 * next,
+ u16 drop_next)
+{
+ u32 n_drop = f->n_elts;
+ while (--n_drop)
+ {
+ (b - n_drop)[0]->error = ESP_ENCRYPT_ERROR_CRYPTO_ENGINE_ERROR;
+ (next - n_drop)[0] = drop_next;
+ }
+ vnet_crypto_async_reset_frame (f);
+}
+