ipsec: esp_decrypt code clean up
[vpp.git] / src / vnet / ipsec / esp_decrypt.c
index 74410a8..94f3204 100644 (file)
@@ -161,6 +161,9 @@ esp_remove_tail (vlib_main_t * vm, vlib_buffer_t * b, vlib_buffer_t * last,
 {
   vlib_buffer_t *before_last = b;
 
+  if (b != last)
+    b->total_length_not_including_first_buffer -= tail;
+
   if (last->current_length > tail)
     {
       last->current_length -= tail;
@@ -178,6 +181,37 @@ esp_remove_tail (vlib_main_t * vm, vlib_buffer_t * b, vlib_buffer_t * last,
   before_last->flags &= ~VLIB_BUFFER_NEXT_PRESENT;
 }
 
+always_inline void
+esp_remove_tail_and_tfc_padding (vlib_main_t *vm, vlib_node_runtime_t *node,
+                                const esp_decrypt_packet_data_t *pd,
+                                vlib_buffer_t *b, vlib_buffer_t *last,
+                                u16 *next, u16 tail, int is_ip6)
+{
+  const u16 total_buffer_length = vlib_buffer_length_in_chain (vm, b);
+  u16 ip_packet_length;
+  if (is_ip6)
+    {
+      const ip6_header_t *ip6 = vlib_buffer_get_current (b);
+      ip_packet_length =
+       clib_net_to_host_u16 (ip6->payload_length) + sizeof (ip6_header_t);
+    }
+  else
+    {
+      const ip4_header_t *ip4 = vlib_buffer_get_current (b);
+      ip_packet_length = clib_net_to_host_u16 (ip4->length);
+    }
+  /* In case of TFC padding, the size of the buffer data needs to be adjusted
+   * to the ip packet length */
+  if (PREDICT_FALSE (total_buffer_length < ip_packet_length + tail))
+    {
+      esp_decrypt_set_next_index (b, node, vm->thread_index,
+                                 ESP_DECRYPT_ERROR_NO_TAIL_SPACE, 0, next,
+                                 ESP_DECRYPT_NEXT_DROP, pd->sa_index);
+      return;
+    }
+  esp_remove_tail (vm, b, last, total_buffer_length - ip_packet_length);
+}
+
 /* ICV is splitted in last two buffers so move it to the last buffer and
    return pointer to it */
 static_always_inline u8 *
@@ -203,9 +237,12 @@ esp_move_icv (vlib_main_t * vm, vlib_buffer_t * first,
   before_last->current_length -= first_sz;
   if (before_last == first)
     pd->current_length -= first_sz;
+  else
+    first->total_length_not_including_first_buffer -= first_sz;
   clib_memset (vlib_buffer_get_tail (before_last), 0, first_sz);
   if (dif)
     dif[0] = first_sz;
+  first->total_length_not_including_first_buffer -= last_sz;
   pd2->lb = before_last;
   pd2->icv_removed = 1;
   pd2->free_buffer_index = before_last->next_buffer;
@@ -457,18 +494,16 @@ esp_decrypt_chain_crypto (vlib_main_t * vm, ipsec_per_thread_data_t * ptd,
   return total_len;
 }
 
-static_always_inline void
-esp_decrypt_prepare_sync_op (vlib_main_t * vm, vlib_node_runtime_t * node,
-                            ipsec_per_thread_data_t * ptd,
-                            vnet_crypto_op_t *** crypto_ops,
-                            vnet_crypto_op_t *** integ_ops,
-                            vnet_crypto_op_t * op,
-                            ipsec_sa_t * sa0, u8 * payload,
-                            u16 len, u8 icv_sz, u8 iv_sz,
-                            esp_decrypt_packet_data_t * pd,
-                            esp_decrypt_packet_data2_t * pd2,
-                            vlib_buffer_t * b, u16 * next, u32 index)
+static_always_inline esp_decrypt_error_t
+esp_decrypt_prepare_sync_op (vlib_main_t *vm, ipsec_per_thread_data_t *ptd,
+                            ipsec_sa_t *sa0, u8 *payload, u16 len, u8 icv_sz,
+                            u8 iv_sz, esp_decrypt_packet_data_t *pd,
+                            esp_decrypt_packet_data2_t *pd2, vlib_buffer_t *b,
+                            u32 index)
 {
+  vnet_crypto_op_t **crypto_ops;
+  vnet_crypto_op_t **integ_ops;
+  vnet_crypto_op_t _op, *op = &_op;
   const u8 esp_sz = sizeof (esp_header_t);
 
   if (PREDICT_TRUE (sa0->integ_op_id != VNET_CRYPTO_OP_NONE))
@@ -485,6 +520,8 @@ esp_decrypt_prepare_sync_op (vlib_main_t * vm, vlib_node_runtime_t * node,
       if (pd->is_chain)
        {
          /* buffer is chained */
+         integ_ops = &ptd->chained_integ_ops;
+
          op->len = pd->current_length;
 
          /* special case when ICV is splitted and needs to be reassembled
@@ -510,8 +547,7 @@ esp_decrypt_prepare_sync_op (vlib_main_t * vm, vlib_node_runtime_t * node,
                    {
                      /* we now have a single buffer of crypto data, adjust
                       * the length (second buffer contains only ICV) */
-                     *integ_ops = &ptd->integ_ops;
-                     *crypto_ops = &ptd->crypto_ops;
+                     integ_ops = &ptd->integ_ops;
                      len = b->current_length;
                      goto out;
                    }
@@ -525,18 +561,16 @@ esp_decrypt_prepare_sync_op (vlib_main_t * vm, vlib_node_runtime_t * node,
          if (esp_decrypt_chain_integ (vm, ptd, pd, pd2, sa0, b, icv_sz,
                                       payload, pd->current_length,
                                       &op->digest, &op->n_chunks, 0) < 0)
-           {
-             esp_decrypt_set_next_index (
-               b, node, vm->thread_index, ESP_DECRYPT_ERROR_NO_BUFFERS, 0,
-               next, ESP_DECRYPT_NEXT_DROP, pd->sa_index);
-             return;
-           }
+           return ESP_DECRYPT_ERROR_NO_BUFFERS;
        }
       else
-       esp_insert_esn (vm, sa0, pd, pd2, &op->len, &op->digest, &len, b,
-                       payload);
+       {
+         integ_ops = &ptd->integ_ops;
+         esp_insert_esn (vm, sa0, pd, pd2, &op->len, &op->digest, &len, b,
+                         payload);
+       }
     out:
-      vec_add_aligned (*(integ_ops[0]), op, 1, CLIB_CACHE_LINE_BYTES);
+      vec_add_aligned (*integ_ops, op, 1, CLIB_CACHE_LINE_BYTES);
     }
 
   payload += esp_sz;
@@ -590,20 +624,26 @@ esp_decrypt_prepare_sync_op (vlib_main_t * vm, vlib_node_runtime_t * node,
          esp_decrypt_chain_crypto (vm, ptd, pd, pd2, sa0, b, icv_sz,
                                    payload, len - pd->iv_sz + pd->icv_sz,
                                    &op->tag, &op->n_chunks);
+         crypto_ops = &ptd->chained_crypto_ops;
+       }
+      else
+       {
+         crypto_ops = &ptd->crypto_ops;
        }
 
-      vec_add_aligned (*(crypto_ops[0]), op, 1, CLIB_CACHE_LINE_BYTES);
+      vec_add_aligned (*crypto_ops, op, 1, CLIB_CACHE_LINE_BYTES);
     }
+
+  return ESP_DECRYPT_ERROR_RX_PKTS;
 }
 
 static_always_inline esp_decrypt_error_t
-esp_decrypt_prepare_async_frame (vlib_main_t *vm, vlib_node_runtime_t *node,
-                                ipsec_per_thread_data_t *ptd,
+esp_decrypt_prepare_async_frame (vlib_main_t *vm, ipsec_per_thread_data_t *ptd,
                                 vnet_crypto_async_frame_t *f, ipsec_sa_t *sa0,
                                 u8 *payload, u16 len, u8 icv_sz, u8 iv_sz,
                                 esp_decrypt_packet_data_t *pd,
                                 esp_decrypt_packet_data2_t *pd2, u32 bi,
-                                vlib_buffer_t *b, u16 *next, u16 async_next)
+                                vlib_buffer_t *b, u16 async_next)
 {
   const u8 esp_sz = sizeof (esp_header_t);
   esp_decrypt_packet_data_t *async_pd = &(esp_post_data (b))->decrypt_data;
@@ -745,6 +785,7 @@ esp_decrypt_post_crypto (vlib_main_t *vm, vlib_node_runtime_t *node,
   const u8 tun_flags = IPSEC_SA_FLAG_IS_TUNNEL | IPSEC_SA_FLAG_IS_TUNNEL_V6;
   u8 pad_length = 0, next_header = 0;
   u16 icv_sz;
+  u64 n_lost;
 
   /*
    * redo the anti-reply check
@@ -753,32 +794,47 @@ esp_decrypt_post_crypto (vlib_main_t *vm, vlib_node_runtime_t *node,
    * check above we did so against the state of the window (W),
    * after packet s-1. So each of the packets in the sequence will be
    * accepted.
-   * This time s will be cheked against Ws-1, s+1 chceked against Ws
-   * (i.e. the window state is updated/advnaced)
-   * so this time the successive s+! packet will be dropped.
+   * This time s will be cheked against Ws-1, s+1 checked against Ws
+   * (i.e. the window state is updated/advanced)
+   * so this time the successive s+1 packet will be dropped.
    * This is a consequence of batching the decrypts. If the
-   * check-dcrypt-advance process was done for each packet it would
+   * check-decrypt-advance process was done for each packet it would
    * be fine. But we batch the decrypts because it's much more efficient
    * to do so in SW and if we offload to HW and the process is async.
    *
    * You're probably thinking, but this means an attacker can send the
-   * above sequence and cause VPP to perform decrpyts that will fail,
+   * above sequence and cause VPP to perform decrypts that will fail,
    * and that's true. But if the attacker can determine s (a valid
    * sequence number in the window) which is non-trivial, it can generate
    * a sequence s, s+1, s+2, s+3, ... s+n and nothing will prevent any
    * implementation, sequential or batching, from decrypting these.
    */
-  if (ipsec_sa_anti_replay_and_sn_advance (sa0, pd->seq, pd->seq_hi, true,
-                                          NULL))
+  if (PREDICT_FALSE (ipsec_sa_is_set_ANTI_REPLAY_HUGE (sa0)))
     {
-      esp_decrypt_set_next_index (b, node, vm->thread_index,
-                                 ESP_DECRYPT_ERROR_REPLAY, 0, next,
-                                 ESP_DECRYPT_NEXT_DROP, pd->sa_index);
-      return;
+      if (ipsec_sa_anti_replay_and_sn_advance (sa0, pd->seq, pd->seq_hi, true,
+                                              NULL, true))
+       {
+         esp_decrypt_set_next_index (b, node, vm->thread_index,
+                                     ESP_DECRYPT_ERROR_REPLAY, 0, next,
+                                     ESP_DECRYPT_NEXT_DROP, pd->sa_index);
+         return;
+       }
+      n_lost = ipsec_sa_anti_replay_advance (sa0, vm->thread_index, pd->seq,
+                                            pd->seq_hi, true);
+    }
+  else
+    {
+      if (ipsec_sa_anti_replay_and_sn_advance (sa0, pd->seq, pd->seq_hi, true,
+                                              NULL, false))
+       {
+         esp_decrypt_set_next_index (b, node, vm->thread_index,
+                                     ESP_DECRYPT_ERROR_REPLAY, 0, next,
+                                     ESP_DECRYPT_NEXT_DROP, pd->sa_index);
+         return;
+       }
+      n_lost = ipsec_sa_anti_replay_advance (sa0, vm->thread_index, pd->seq,
+                                            pd->seq_hi, false);
     }
-
-  u64 n_lost =
-    ipsec_sa_anti_replay_advance (sa0, vm->thread_index, pd->seq, pd->seq_hi);
 
   vlib_prefetch_simple_counter (&ipsec_sa_err_counters[IPSEC_SA_ERROR_LOST],
                                vm->thread_index, pd->sa_index);
@@ -841,8 +897,7 @@ esp_decrypt_post_crypto (vlib_main_t *vm, vlib_node_runtime_t *node,
   u16 tail = sizeof (esp_footer_t) + pad_length + icv_sz;
   u16 tail_orig = sizeof (esp_footer_t) + pad_length + pd->icv_sz;
   b->flags &=
-    ~(VLIB_BUFFER_TOTAL_LENGTH_VALID | VNET_BUFFER_F_L4_CHECKSUM_COMPUTED |
-      VNET_BUFFER_F_L4_CHECKSUM_CORRECT);
+    ~(VNET_BUFFER_F_L4_CHECKSUM_COMPUTED | VNET_BUFFER_F_L4_CHECKSUM_CORRECT);
 
   if ((pd->flags & tun_flags) == 0 && !is_tun) /* transport mode */
     {
@@ -892,14 +947,16 @@ esp_decrypt_post_crypto (vlib_main_t *vm, vlib_node_runtime_t *node,
          next[0] = ESP_DECRYPT_NEXT_IP4_INPUT;
          b->current_data = pd->current_data + adv;
          b->current_length = pd->current_length - adv;
-         esp_remove_tail (vm, b, lb, tail);
+         esp_remove_tail_and_tfc_padding (vm, node, pd, b, lb, next, tail,
+                                          false);
        }
       else if (next_header == IP_PROTOCOL_IPV6)
        {
          next[0] = ESP_DECRYPT_NEXT_IP6_INPUT;
          b->current_data = pd->current_data + adv;
          b->current_length = pd->current_length - adv;
-         esp_remove_tail (vm, b, lb, tail);
+         esp_remove_tail_and_tfc_padding (vm, node, pd, b, lb, next, tail,
+                                          true);
        }
       else if (next_header == IP_PROTOCOL_MPLS_IN_IP)
        {
@@ -1046,9 +1103,7 @@ esp_decrypt_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
   u32 current_sa_index = ~0, current_sa_bytes = 0, current_sa_pkts = 0;
   const u8 esp_sz = sizeof (esp_header_t);
   ipsec_sa_t *sa0 = 0;
-  vnet_crypto_op_t _op, *op = &_op;
-  vnet_crypto_op_t **crypto_ops;
-  vnet_crypto_op_t **integ_ops;
+  bool anti_replay_result;
   int is_async = im->async_mode;
   vnet_crypto_async_op_id_t async_op = ~0;
   vnet_crypto_async_frame_t *async_frames[VNET_CRYPTO_ASYNC_OP_N_IDS];
@@ -1150,21 +1205,23 @@ esp_decrypt_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
          /* find last buffer in the chain */
          while (pd2->lb->flags & VLIB_BUFFER_NEXT_PRESENT)
            pd2->lb = vlib_get_buffer (vm, pd2->lb->next_buffer);
+       }
 
-         crypto_ops = &ptd->chained_crypto_ops;
-         integ_ops = &ptd->chained_integ_ops;
+      pd->current_length = b[0]->current_length;
+
+      /* anti-reply check */
+      if (PREDICT_FALSE (ipsec_sa_is_set_ANTI_REPLAY_HUGE (sa0)))
+       {
+         anti_replay_result = ipsec_sa_anti_replay_and_sn_advance (
+           sa0, pd->seq, ~0, false, &pd->seq_hi, true);
        }
       else
        {
-         crypto_ops = &ptd->crypto_ops;
-         integ_ops = &ptd->integ_ops;
+         anti_replay_result = ipsec_sa_anti_replay_and_sn_advance (
+           sa0, pd->seq, ~0, false, &pd->seq_hi, false);
        }
 
-      pd->current_length = b[0]->current_length;
-
-      /* anti-reply check */
-      if (ipsec_sa_anti_replay_and_sn_advance (sa0, pd->seq, ~0, false,
-                                              &pd->seq_hi))
+      if (anti_replay_result)
        {
          err = ESP_DECRYPT_ERROR_REPLAY;
          esp_decrypt_set_next_index (b[0], node, thread_index, err, n_noop,
@@ -1211,9 +1268,8 @@ esp_decrypt_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
            }
 
          err = esp_decrypt_prepare_async_frame (
-           vm, node, ptd, async_frames[async_op], sa0, payload, len,
-           cpd.icv_sz, cpd.iv_sz, pd, pd2, from[b - bufs], b[0], async_next,
-           async_next_node);
+           vm, ptd, async_frames[async_op], sa0, payload, len, cpd.icv_sz,
+           cpd.iv_sz, pd, pd2, from[b - bufs], b[0], async_next_node);
          if (ESP_DECRYPT_ERROR_RX_PKTS != err)
            {
              esp_decrypt_set_next_index (
@@ -1222,9 +1278,17 @@ esp_decrypt_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
            }
        }
       else
-       esp_decrypt_prepare_sync_op (
-         vm, node, ptd, &crypto_ops, &integ_ops, op, sa0, payload, len,
-         cpd.icv_sz, cpd.iv_sz, pd, pd2, b[0], sync_next, n_sync);
+       {
+         err = esp_decrypt_prepare_sync_op (vm, ptd, sa0, payload, len,
+                                            cpd.icv_sz, cpd.iv_sz, pd, pd2,
+                                            b[0], n_sync);
+         if (err != ESP_DECRYPT_ERROR_RX_PKTS)
+           {
+             esp_decrypt_set_next_index (b[0], node, thread_index, err, 0,
+                                         sync_next, ESP_DECRYPT_NEXT_DROP,
+                                         current_sa_index);
+           }
+       }
       /* next */
     next:
       if (ESP_DECRYPT_ERROR_RX_PKTS != err)
@@ -1483,7 +1547,6 @@ VLIB_NODE_FN (esp6_decrypt_tun_post_node) (vlib_main_t * vm,
   return esp_decrypt_post_inline (vm, node, from_frame, 1, 1);
 }
 
-/* *INDENT-OFF* */
 VLIB_REGISTER_NODE (esp4_decrypt_node) = {
   .name = "esp4-decrypt",
   .vector_size = sizeof (u32),
@@ -1607,7 +1670,6 @@ VLIB_REGISTER_NODE (esp6_decrypt_tun_post_node) = {
 
   .sibling_of = "esp6-decrypt-tun",
 };
-/* *INDENT-ON* */
 
 #ifndef CLIB_MARCH_VARIANT