ipsec: manually binding an SA to a worker
[vpp.git] / src / vnet / ipsec / ipsec_sa.h
index 14461ad..a9abfdb 100644 (file)
 #define __IPSEC_SPD_SA_H__
 
 #include <vlib/vlib.h>
+#include <vppinfra/pcg.h>
 #include <vnet/crypto/crypto.h>
 #include <vnet/ip/ip.h>
 #include <vnet/fib/fib_node.h>
 #include <vnet/tunnel/tunnel.h>
 
-#define foreach_ipsec_crypto_alg    \
-  _ (0, NONE, "none")               \
-  _ (1, AES_CBC_128, "aes-cbc-128") \
-  _ (2, AES_CBC_192, "aes-cbc-192") \
-  _ (3, AES_CBC_256, "aes-cbc-256") \
-  _ (4, AES_CTR_128, "aes-ctr-128") \
-  _ (5, AES_CTR_192, "aes-ctr-192") \
-  _ (6, AES_CTR_256, "aes-ctr-256") \
-  _ (7, AES_GCM_128, "aes-gcm-128") \
-  _ (8, AES_GCM_192, "aes-gcm-192") \
-  _ (9, AES_GCM_256, "aes-gcm-256") \
-  _ (10, DES_CBC, "des-cbc")        \
-  _ (11, 3DES_CBC, "3des-cbc")
+#define ESP_MAX_ICV_SIZE   (32)
+#define ESP_MAX_IV_SIZE           (16)
+#define ESP_MAX_BLOCK_SIZE (16)
+
+#define foreach_ipsec_crypto_alg                                              \
+  _ (0, NONE, "none")                                                         \
+  _ (1, AES_CBC_128, "aes-cbc-128")                                           \
+  _ (2, AES_CBC_192, "aes-cbc-192")                                           \
+  _ (3, AES_CBC_256, "aes-cbc-256")                                           \
+  _ (4, AES_CTR_128, "aes-ctr-128")                                           \
+  _ (5, AES_CTR_192, "aes-ctr-192")                                           \
+  _ (6, AES_CTR_256, "aes-ctr-256")                                           \
+  _ (7, AES_GCM_128, "aes-gcm-128")                                           \
+  _ (8, AES_GCM_192, "aes-gcm-192")                                           \
+  _ (9, AES_GCM_256, "aes-gcm-256")                                           \
+  _ (10, DES_CBC, "des-cbc")                                                  \
+  _ (11, 3DES_CBC, "3des-cbc")                                                \
+  _ (12, CHACHA20_POLY1305, "chacha20-poly1305")
 
 typedef enum
 {
@@ -53,6 +59,9 @@ typedef enum
     (_alg == IPSEC_CRYPTO_ALG_AES_CTR_192) ||                                 \
     (_alg == IPSEC_CRYPTO_ALG_AES_CTR_256)))
 
+#define IPSEC_CRYPTO_ALG_CTR_AEAD_OTHERS(_alg)                                \
+  (_alg == IPSEC_CRYPTO_ALG_CHACHA20_POLY1305)
+
 #define foreach_ipsec_integ_alg                                            \
   _ (0, NONE, "none")                                                      \
   _ (1, MD5_96, "md5-96")           /* RFC2403 */                          \
@@ -102,7 +111,8 @@ typedef struct ipsec_key_t_
   _ (64, IS_INBOUND, "inbound")                                               \
   _ (128, IS_AEAD, "aead")                                                    \
   _ (256, IS_CTR, "ctr")                                                      \
-  _ (512, IS_ASYNC, "async")
+  _ (512, IS_ASYNC, "async")                                                  \
+  _ (1024, NO_ALGO_NO_DROP, "no-algo-no-drop")
 
 typedef enum ipsec_sad_flags_t_
 {
@@ -113,51 +123,60 @@ typedef enum ipsec_sad_flags_t_
 
 STATIC_ASSERT (sizeof (ipsec_sa_flags_t) == 2, "IPSEC SA flags != 2 byte");
 
+#define foreach_ipsec_sa_err                                                  \
+  _ (0, LOST, lost, "packets lost")                                           \
+  _ (1, HANDOFF, handoff, "hand-off")                                         \
+  _ (2, INTEG_ERROR, integ_error, "Integrity check failed")                   \
+  _ (3, DECRYPTION_FAILED, decryption_failed, "Decryption failed")            \
+  _ (4, CRYPTO_ENGINE_ERROR, crypto_engine_error,                             \
+     "crypto engine error (dropped)")                                         \
+  _ (5, REPLAY, replay, "SA replayed packet")                                 \
+  _ (6, RUNT, runt, "undersized packet")                                      \
+  _ (7, NO_BUFFERS, no_buffers, "no buffers (dropped)")                       \
+  _ (8, OVERSIZED_HEADER, oversized_header,                                   \
+     "buffer with oversized header (dropped)")                                \
+  _ (9, NO_TAIL_SPACE, no_tail_space,                                         \
+     "no enough buffer tail space (dropped)")                                 \
+  _ (10, TUN_NO_PROTO, tun_no_proto, "no tunnel protocol")                    \
+  _ (11, UNSUP_PAYLOAD, unsup_payload, "unsupported payload")                 \
+  _ (12, SEQ_CYCLED, seq_cycled, "sequence number cycled (dropped)")          \
+  _ (13, CRYPTO_QUEUE_FULL, crypto_queue_full, "crypto queue full (dropped)") \
+  _ (14, NO_ENCRYPTION, no_encryption, "no Encrypting SA (dropped)")          \
+  _ (15, DROP_FRAGMENTS, drop_fragments, "IP fragments drop")
+
+typedef enum
+{
+#define _(v, f, s, d) IPSEC_SA_ERROR_##f = v,
+  foreach_ipsec_sa_err
+#undef _
+    IPSEC_SA_N_ERRORS,
+} __clib_packed ipsec_sa_err_t;
+
 typedef struct
 {
   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
 
-  /* flags */
-  ipsec_sa_flags_t flags;
-
-  u8 crypto_iv_size;
-  u8 esp_block_align;
-  u8 integ_icv_size;
-
-  u8 __pad1[3];
-
-  u32 thread_index;
+  clib_pcg64i_random_t iv_prng;
 
-  u32 spi;
-  u32 seq;
-  u32 seq_hi;
   u64 replay_window;
-  u64 ctr_iv_counter;
   dpo_id_t dpo;
 
   vnet_crypto_key_index_t crypto_key_index;
   vnet_crypto_key_index_t integ_key_index;
 
-  /* Union data shared by sync and async ops, updated when mode is
-   * changed. */
-  union
-  {
-    struct
-    {
-      vnet_crypto_op_id_t crypto_enc_op_id:16;
-      vnet_crypto_op_id_t crypto_dec_op_id:16;
-      vnet_crypto_op_id_t integ_op_id:16;
-    };
+  u32 spi;
+  u32 seq;
+  u32 seq_hi;
 
-    struct
-    {
-      vnet_crypto_async_op_id_t crypto_async_enc_op_id:16;
-      vnet_crypto_async_op_id_t crypto_async_dec_op_id:16;
-      vnet_crypto_key_index_t linked_key_index;
-    };
+  u16 crypto_enc_op_id;
+  u16 crypto_dec_op_id;
+  u16 integ_op_id;
+  ipsec_sa_flags_t flags;
+  u16 thread_index;
 
-    u64 crypto_op_data;
-  };
+  u16 integ_icv_size : 6;
+  u16 crypto_iv_size : 5;
+  u16 esp_block_align : 5;
 
   CLIB_CACHE_LINE_ALIGN_MARK (cacheline1);
 
@@ -179,30 +198,7 @@ typedef struct
     CLIB_CACHE_LINE_ALIGN_MARK (cacheline2);
 
   /* Elements with u64 size multiples */
-  union
-  {
-    struct
-    {
-      vnet_crypto_op_id_t crypto_enc_op_id:16;
-      vnet_crypto_op_id_t crypto_dec_op_id:16;
-      vnet_crypto_op_id_t integ_op_id:16;
-    };
-    u64 data;
-  } sync_op_data;
-
-  union
-  {
-    struct
-    {
-      vnet_crypto_async_op_id_t crypto_async_enc_op_id:16;
-      vnet_crypto_async_op_id_t crypto_async_dec_op_id:16;
-      vnet_crypto_key_index_t linked_key_index;
-    };
-    u64 data;
-  } async_op_data;
-
   tunnel_t tunnel;
-
   fib_node_t node;
 
   /* elements with u32 size */
@@ -210,6 +206,16 @@ typedef struct
   u32 stat_index;
   vnet_crypto_alg_t integ_calg;
   vnet_crypto_alg_t crypto_calg;
+  u32 crypto_sync_key_index;
+  u32 integ_sync_key_index;
+  u32 crypto_async_key_index;
+
+  /* elements with u16 size */
+  u16 crypto_sync_enc_op_id;
+  u16 crypto_sync_dec_op_id;
+  u16 integ_sync_op_id;
+  u16 crypto_async_enc_op_id;
+  u16 crypto_async_dec_op_id;
 
   /* else u8 packed */
   ipsec_crypto_alg_t crypto_alg;
@@ -219,6 +225,10 @@ typedef struct
   ipsec_key_t crypto_key;
 } ipsec_sa_t;
 
+STATIC_ASSERT (VNET_CRYPTO_N_OP_IDS < (1 << 16), "crypto ops overflow");
+STATIC_ASSERT (ESP_MAX_ICV_SIZE < (1 << 6), "integer icv overflow");
+STATIC_ASSERT (ESP_MAX_IV_SIZE < (1 << 5), "esp iv overflow");
+STATIC_ASSERT (ESP_MAX_BLOCK_SIZE < (1 << 5), "esp alignment overflow");
 STATIC_ASSERT_OFFSET_OF (ipsec_sa_t, cacheline1, CLIB_CACHE_LINE_BYTES);
 STATIC_ASSERT_OFFSET_OF (ipsec_sa_t, cacheline2, 2 * CLIB_CACHE_LINE_BYTES);
 
@@ -261,15 +271,19 @@ foreach_ipsec_sa_flags
  * SA packet & bytes counters
  */
 extern vlib_combined_counter_main_t ipsec_sa_counters;
+extern vlib_simple_counter_main_t ipsec_sa_err_counters[IPSEC_SA_N_ERRORS];
 
 extern void ipsec_mk_key (ipsec_key_t * key, const u8 * data, u8 len);
 
+extern int ipsec_sa_update (u32 id, u16 src_port, u16 dst_port,
+                           const tunnel_t *tun, bool is_tun);
 extern int
 ipsec_sa_add_and_lock (u32 id, u32 spi, ipsec_protocol_t proto,
                       ipsec_crypto_alg_t crypto_alg, const ipsec_key_t *ck,
                       ipsec_integ_alg_t integ_alg, const ipsec_key_t *ik,
                       ipsec_sa_flags_t flags, u32 salt, u16 src_port,
                       u16 dst_port, const tunnel_t *tun, u32 *sa_out_index);
+extern int ipsec_sa_bind (u32 id, u32 worker, bool bind);
 extern index_t ipsec_sa_find_and_lock (u32 id);
 extern int ipsec_sa_unlock_id (u32 id);
 extern void ipsec_sa_unlock (index_t sai);
@@ -279,6 +293,7 @@ extern void ipsec_sa_set_crypto_alg (ipsec_sa_t * sa,
                                     ipsec_crypto_alg_t crypto_alg);
 extern void ipsec_sa_set_integ_alg (ipsec_sa_t * sa,
                                    ipsec_integ_alg_t integ_alg);
+extern void ipsec_sa_set_async_mode (ipsec_sa_t *sa, int is_enabled);
 
 typedef walk_rc_t (*ipsec_sa_walk_cb_t) (ipsec_sa_t * sa, void *ctx);
 extern void ipsec_sa_walk (ipsec_sa_walk_cb_t cd, void *ctx);
@@ -522,6 +537,48 @@ ipsec_sa_anti_replay_and_sn_advance (const ipsec_sa_t *sa, u32 seq,
   return 0;
 }
 
+always_inline u32
+ipsec_sa_anti_replay_window_shift (ipsec_sa_t *sa, u32 inc)
+{
+  u32 n_lost = 0;
+
+  if (inc < IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE)
+    {
+      if (sa->seq > IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE)
+       {
+         /*
+          * count how many holes there are in the portion
+          * of the window that we will right shift of the end
+          * as a result of this increments
+          */
+         u64 mask = (((u64) 1 << inc) - 1) << (BITS (u64) - inc);
+         u64 old = sa->replay_window & mask;
+         /* the number of packets we saw in this section of the window */
+         u64 seen = count_set_bits (old);
+
+         /*
+          * the number we missed is the size of the window section
+          * minus the number we saw.
+          */
+         n_lost = inc - seen;
+       }
+      sa->replay_window = ((sa->replay_window) << inc) | 1;
+    }
+  else
+    {
+      /* holes in the replay window are lost packets */
+      n_lost = BITS (u64) - count_set_bits (sa->replay_window);
+
+      /* any sequence numbers that now fall outside the window
+       * are forever lost */
+      n_lost += inc - IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE;
+
+      sa->replay_window = 1;
+    }
+
+  return (n_lost);
+}
+
 /*
  * Anti replay window advance
  *  inputs need to be in host byte order.
@@ -531,9 +588,11 @@ ipsec_sa_anti_replay_and_sn_advance (const ipsec_sa_t *sa, u32 seq,
  * However, updating the window is trivial, so we do it anyway to save
  * the branch cost.
  */
-always_inline void
-ipsec_sa_anti_replay_advance (ipsec_sa_t *sa, u32 seq, u32 hi_seq)
+always_inline u64
+ipsec_sa_anti_replay_advance (ipsec_sa_t *sa, u32 thread_index, u32 seq,
+                             u32 hi_seq)
 {
+  u64 n_lost = 0;
   u32 pos;
 
   if (ipsec_sa_is_set_USE_ESN (sa))
@@ -543,19 +602,13 @@ ipsec_sa_anti_replay_advance (ipsec_sa_t *sa, u32 seq, u32 hi_seq)
       if (wrap == 0 && seq > sa->seq)
        {
          pos = seq - sa->seq;
-         if (pos < IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE)
-           sa->replay_window = ((sa->replay_window) << pos) | 1;
-         else
-           sa->replay_window = 1;
+         n_lost = ipsec_sa_anti_replay_window_shift (sa, pos);
          sa->seq = seq;
        }
       else if (wrap > 0)
        {
          pos = ~seq + sa->seq + 1;
-         if (pos < IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE)
-           sa->replay_window = ((sa->replay_window) << pos) | 1;
-         else
-           sa->replay_window = 1;
+         n_lost = ipsec_sa_anti_replay_window_shift (sa, pos);
          sa->seq = seq;
          sa->seq_hi = hi_seq;
        }
@@ -575,10 +628,7 @@ ipsec_sa_anti_replay_advance (ipsec_sa_t *sa, u32 seq, u32 hi_seq)
       if (seq > sa->seq)
        {
          pos = seq - sa->seq;
-         if (pos < IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE)
-           sa->replay_window = ((sa->replay_window) << pos) | 1;
-         else
-           sa->replay_window = 1;
+         n_lost = ipsec_sa_anti_replay_window_shift (sa, pos);
          sa->seq = seq;
        }
       else
@@ -587,6 +637,8 @@ ipsec_sa_anti_replay_advance (ipsec_sa_t *sa, u32 seq, u32 hi_seq)
          sa->replay_window |= (1ULL << pos);
        }
     }
+
+  return n_lost;
 }
 
 
@@ -594,8 +646,8 @@ ipsec_sa_anti_replay_advance (ipsec_sa_t *sa, u32 seq, u32 hi_seq)
  * Makes choice for thread_id should be assigned.
  *  if input ~0, gets random worker_id based on unix_time_now_nsec
 */
-always_inline u32
-ipsec_sa_assign_thread (u32 thread_id)
+always_inline u16
+ipsec_sa_assign_thread (u16 thread_id)
 {
   return ((thread_id) ? thread_id
          : (unix_time_now_nsec () % vlib_num_workers ()) + 1);