ipsec: huge anti-replay window support 65/37865/23
authorMaxime Peim <mpeim@cisco.com>
Thu, 22 Dec 2022 11:26:57 +0000 (11:26 +0000)
committerBeno�t Ganne <bganne@cisco.com>
Mon, 30 Oct 2023 15:23:13 +0000 (15:23 +0000)
Type: improvement

Since RFC4303 does not specify the anti-replay window size, VPP should
support multiple window size. It is done through a clib_bitmap.

Signed-off-by: Maxime Peim <mpeim@cisco.com>
Change-Id: I3dfe30efd20018e345418bef298ec7cec19b1cfc

17 files changed:
src/plugins/ikev2/ikev2.c
src/plugins/unittest/ipsec_test.c
src/vnet/ipsec/ah_decrypt.c
src/vnet/ipsec/esp_decrypt.c
src/vnet/ipsec/ipsec.api
src/vnet/ipsec/ipsec_api.c
src/vnet/ipsec/ipsec_cli.c
src/vnet/ipsec/ipsec_format.c
src/vnet/ipsec/ipsec_sa.c
src/vnet/ipsec/ipsec_sa.h
src/vnet/ipsec/ipsec_test.c
src/vnet/ipsec/ipsec_types.api
src/vppinfra/bitmap.h
test/template_ipsec.py
test/test_ipsec_api.py
test/test_ipsec_esp.py
test/vpp_ipsec.py

index 3e80873..ad36068 100644 (file)
@@ -2041,7 +2041,7 @@ ikev2_add_tunnel_from_main (ikev2_add_ipsec_tunnel_args_t * a)
   rv = ipsec_sa_add_and_lock (a->local_sa_id, a->local_spi, IPSEC_PROTOCOL_ESP,
                              a->encr_type, &a->loc_ckey, a->integ_type,
                              &a->loc_ikey, a->flags, a->salt_local,
-                             a->src_port, a->dst_port, &tun_out, NULL);
+                             a->src_port, a->dst_port, 0, &tun_out, NULL);
   if (rv)
     goto err0;
 
@@ -2049,7 +2049,7 @@ ikev2_add_tunnel_from_main (ikev2_add_ipsec_tunnel_args_t * a)
     a->remote_sa_id, a->remote_spi, IPSEC_PROTOCOL_ESP, a->encr_type,
     &a->rem_ckey, a->integ_type, &a->rem_ikey,
     (a->flags | IPSEC_SA_FLAG_IS_INBOUND), a->salt_remote,
-    a->ipsec_over_udp_port, a->ipsec_over_udp_port, &tun_in, NULL);
+    a->ipsec_over_udp_port, a->ipsec_over_udp_port, 0, &tun_in, NULL);
   if (rv)
     goto err1;
 
index 55fd031..bb7f2a8 100644 (file)
@@ -18,8 +18,8 @@
 #include <vnet/ipsec/ipsec_output.h>
 
 static clib_error_t *
-test_ipsec_command_fn (vlib_main_t * vm,
-                      unformat_input_t * input, vlib_cli_command_t * cmd)
+test_ipsec_command_fn (vlib_main_t *vm, unformat_input_t *input,
+                      vlib_cli_command_t *cmd)
 {
   u64 seq_num;
   u32 sa_id;
@@ -48,12 +48,18 @@ test_ipsec_command_fn (vlib_main_t * vm,
       sa->seq = seq_num & 0xffffffff;
       sa->seq_hi = seq_num >> 32;
 
+      /* clear the window */
+      if (ipsec_sa_is_set_ANTI_REPLAY_HUGE (sa))
+       clib_bitmap_zero (sa->replay_window_huge);
+      else
+       sa->replay_window = 0;
+
       ipsec_sa_unlock (sa_index);
     }
   else
     {
-      return clib_error_return (0, "unknown SA `%U'",
-                               format_unformat_error, input);
+      return clib_error_return (0, "unknown SA `%U'", format_unformat_error,
+                               input);
     }
 
   return (NULL);
@@ -134,7 +140,7 @@ test_ipsec_spd_outbound_perf_command_fn (vlib_main_t *vm,
   /* creating a new SA */
   rv = ipsec_sa_add_and_lock (sa_id, spi, proto, crypto_alg, &ck, integ_alg,
                              &ik, sa_flags, clib_host_to_net_u32 (salt),
-                             udp_src, udp_dst, &tun, &sai);
+                             udp_src, udp_dst, 0, &tun, &sai);
   if (rv)
     {
       err = clib_error_return (0, "create sa failure");
@@ -368,8 +374,7 @@ VLIB_CLI_COMMAND (test_ipsec_spd_perf_command, static) = {
 };
 
 /* *INDENT-OFF* */
-VLIB_CLI_COMMAND (test_ipsec_command, static) =
-{
+VLIB_CLI_COMMAND (test_ipsec_command, static) = {
   .path = "test ipsec",
   .short_help = "test ipsec sa <ID> seq-num <VALUE>",
   .function = test_ipsec_command_fn,
index 5f98693..62a6b70 100644 (file)
@@ -128,6 +128,7 @@ ah_decrypt_inline (vlib_main_t * vm,
   from = vlib_frame_vector_args (from_frame);
   n_left = from_frame->n_vectors;
   ipsec_sa_t *sa0 = 0;
+  bool anti_replay_result;
   u32 current_sa_index = ~0, current_sa_bytes = 0, current_sa_pkts = 0;
 
   clib_memset (pkt_data, 0, VLIB_FRAME_SIZE * sizeof (pkt_data[0]));
@@ -201,8 +202,17 @@ ah_decrypt_inline (vlib_main_t * vm,
       pd->seq = clib_host_to_net_u32 (ah0->seq_no);
 
       /* anti-replay check */
-      if (ipsec_sa_anti_replay_and_sn_advance (sa0, pd->seq, ~0, false,
-                                              &pd->seq_hi))
+      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
+       {
+         anti_replay_result = ipsec_sa_anti_replay_and_sn_advance (
+           sa0, pd->seq, ~0, false, &pd->seq_hi, false);
+       }
+      if (anti_replay_result)
        {
          ah_decrypt_set_next_index (b[0], node, vm->thread_index,
                                     AH_DECRYPT_ERROR_REPLAY, 0, next,
@@ -306,16 +316,32 @@ ah_decrypt_inline (vlib_main_t * vm,
       if (PREDICT_TRUE (sa0->integ_alg != IPSEC_INTEG_ALG_NONE))
        {
          /* redo the anti-reply check. see esp_decrypt for details */
-         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)))
            {
-             ah_decrypt_set_next_index (b[0], node, vm->thread_index,
-                                        AH_DECRYPT_ERROR_REPLAY, 0, next,
-                                        AH_DECRYPT_NEXT_DROP, pd->sa_index);
-             goto trace;
+             if (ipsec_sa_anti_replay_and_sn_advance (
+                   sa0, pd->seq, pd->seq_hi, true, NULL, true))
+               {
+                 ah_decrypt_set_next_index (
+                   b[0], node, vm->thread_index, AH_DECRYPT_ERROR_REPLAY, 0,
+                   next, AH_DECRYPT_NEXT_DROP, pd->sa_index);
+                 goto trace;
+               }
+             n_lost = ipsec_sa_anti_replay_advance (
+               sa0, 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))
+               {
+                 ah_decrypt_set_next_index (
+                   b[0], node, vm->thread_index, AH_DECRYPT_ERROR_REPLAY, 0,
+                   next, AH_DECRYPT_NEXT_DROP, pd->sa_index);
+                 goto trace;
+               }
+             n_lost = ipsec_sa_anti_replay_advance (
+               sa0, thread_index, pd->seq, pd->seq_hi, false);
            }
-         n_lost = ipsec_sa_anti_replay_advance (sa0, thread_index, pd->seq,
-                                                pd->seq_hi);
          vlib_prefetch_simple_counter (
            &ipsec_sa_err_counters[IPSEC_SA_ERROR_LOST], thread_index,
            pd->sa_index);
index 74410a8..7b74e6e 100644 (file)
@@ -745,6 +745,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 +754,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);
@@ -1046,6 +1062,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;
+  bool anti_replay_result;
   vnet_crypto_op_t _op, *op = &_op;
   vnet_crypto_op_t **crypto_ops;
   vnet_crypto_op_t **integ_ops;
@@ -1163,8 +1180,18 @@ esp_decrypt_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
       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 (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
+       {
+         anti_replay_result = ipsec_sa_anti_replay_and_sn_advance (
+           sa0, pd->seq, ~0, false, &pd->seq_hi, false);
+       }
+
+      if (anti_replay_result)
        {
          err = ESP_DECRYPT_ERROR_REPLAY;
          esp_decrypt_set_next_index (b[0], node, thread_index, err, n_noop,
index ad1d216..68efe8f 100644 (file)
@@ -191,12 +191,21 @@ define ipsec_sad_entry_add_del_v3
   bool is_add;
   vl_api_ipsec_sad_entry_v3_t entry;
 };
+
 define ipsec_sad_entry_add
 {
   u32 client_index;
   u32 context;
   vl_api_ipsec_sad_entry_v3_t entry;
 };
+
+define ipsec_sad_entry_add_v2
+{
+  u32 client_index;
+  u32 context;
+  vl_api_ipsec_sad_entry_v4_t entry;
+};
+
 autoreply define ipsec_sad_entry_del
 {
   u32 client_index;
@@ -273,6 +282,7 @@ define ipsec_sad_entry_add_del_v3_reply
   i32 retval;
   u32 stat_index;
 };
+
 define ipsec_sad_entry_add_reply
 {
   u32 context;
@@ -280,6 +290,13 @@ define ipsec_sad_entry_add_reply
   u32 stat_index;
 };
 
+define ipsec_sad_entry_add_v2_reply
+{
+  u32 context;
+  i32 retval;
+  u32 stat_index;
+};
+
 /** \brief Add or Update Protection for a tunnel with IPSEC
 
     Tunnel protection directly associates an SA with all packets
@@ -468,6 +485,12 @@ define ipsec_sa_v4_dump
   u32 context;
   u32 sa_id;
 };
+define ipsec_sa_v5_dump
+{
+  u32 client_index;
+  u32 context;
+  u32 sa_id;
+};
 
 /** \brief IPsec security association database response
     @param context - sender context which was passed in the request
@@ -528,7 +551,17 @@ define ipsec_sa_v4_details {
   u64 seq_outbound;
   u64 last_seq_inbound;
   u64 replay_window;
+  u32 thread_index;
+  u32 stat_index;
+};
+define ipsec_sa_v5_details {
+  u32 context;
+  vl_api_ipsec_sad_entry_v4_t entry;
 
+  vl_api_interface_index_t sw_if_index;
+  u64 seq_outbound;
+  u64 last_seq_inbound;
+  u64 replay_window;
   u32 thread_index;
   u32 stat_index;
 };
index 7fc68f6..6e7308d 100644 (file)
@@ -382,10 +382,10 @@ static void vl_api_ipsec_sad_entry_add_del_t_handler
   ip_address_decode2 (&mp->entry.tunnel_src, &tun.t_src);
   ip_address_decode2 (&mp->entry.tunnel_dst, &tun.t_dst);
 
-  rv = ipsec_sa_add_and_lock (id, spi, proto, crypto_alg, &crypto_key,
-                             integ_alg, &integ_key, flags, mp->entry.salt,
-                             htons (mp->entry.udp_src_port),
-                             htons (mp->entry.udp_dst_port), &tun, &sa_index);
+  rv = ipsec_sa_add_and_lock (
+    id, spi, proto, crypto_alg, &crypto_key, integ_alg, &integ_key, flags,
+    mp->entry.salt, htons (mp->entry.udp_src_port),
+    htons (mp->entry.udp_dst_port), 0, &tun, &sa_index);
 
 out:
   /* *INDENT-OFF* */
@@ -456,10 +456,10 @@ static void vl_api_ipsec_sad_entry_add_del_v2_t_handler
   ip_address_decode2 (&mp->entry.tunnel_src, &tun.t_src);
   ip_address_decode2 (&mp->entry.tunnel_dst, &tun.t_dst);
 
-    rv = ipsec_sa_add_and_lock (
-      id, spi, proto, crypto_alg, &crypto_key, integ_alg, &integ_key, flags,
-      mp->entry.salt, htons (mp->entry.udp_src_port),
-      htons (mp->entry.udp_dst_port), &tun, &sa_index);
+  rv = ipsec_sa_add_and_lock (
+    id, spi, proto, crypto_alg, &crypto_key, integ_alg, &integ_key, flags,
+    mp->entry.salt, htons (mp->entry.udp_src_port),
+    htons (mp->entry.udp_dst_port), 0, &tun, &sa_index);
 
 out:
   /* *INDENT-OFF* */
@@ -514,10 +514,10 @@ ipsec_sad_entry_add_v3 (const vl_api_ipsec_sad_entry_v3_t *entry,
   ipsec_key_decode (&entry->crypto_key, &crypto_key);
   ipsec_key_decode (&entry->integrity_key, &integ_key);
 
-  return ipsec_sa_add_and_lock (id, spi, proto, crypto_alg, &crypto_key,
-                               integ_alg, &integ_key, flags, entry->salt,
-                               htons (entry->udp_src_port),
-                               htons (entry->udp_dst_port), &tun, sa_index);
+  return ipsec_sa_add_and_lock (
+    id, spi, proto, crypto_alg, &crypto_key, integ_alg, &integ_key, flags,
+    entry->salt, htons (entry->udp_src_port), htons (entry->udp_dst_port), 0,
+    &tun, sa_index);
 }
 
 static void
@@ -543,6 +543,56 @@ vl_api_ipsec_sad_entry_add_del_v3_t_handler (
                { rmp->stat_index = htonl (sa_index); });
 }
 
+static int
+ipsec_sad_entry_add_v4 (const vl_api_ipsec_sad_entry_v4_t *entry,
+                       u32 *sa_index)
+{
+  ipsec_key_t crypto_key, integ_key;
+  ipsec_crypto_alg_t crypto_alg;
+  ipsec_integ_alg_t integ_alg;
+  ipsec_protocol_t proto;
+  ipsec_sa_flags_t flags;
+  u32 id, spi;
+  tunnel_t tun = { 0 };
+  int rv;
+
+  id = ntohl (entry->sad_id);
+  spi = ntohl (entry->spi);
+
+  rv = ipsec_proto_decode (entry->protocol, &proto);
+
+  if (rv)
+    return rv;
+
+  rv = ipsec_crypto_algo_decode (entry->crypto_algorithm, &crypto_alg);
+
+  if (rv)
+    return rv;
+
+  rv = ipsec_integ_algo_decode (entry->integrity_algorithm, &integ_alg);
+
+  if (rv)
+    return rv;
+
+  flags = ipsec_sa_flags_decode (entry->flags);
+
+  if (flags & IPSEC_SA_FLAG_IS_TUNNEL)
+    {
+      rv = tunnel_decode (&entry->tunnel, &tun);
+
+      if (rv)
+       return rv;
+    }
+
+  ipsec_key_decode (&entry->crypto_key, &crypto_key);
+  ipsec_key_decode (&entry->integrity_key, &integ_key);
+
+  return ipsec_sa_add_and_lock (
+    id, spi, proto, crypto_alg, &crypto_key, integ_alg, &integ_key, flags,
+    entry->salt, htons (entry->udp_src_port), htons (entry->udp_dst_port),
+    ntohl (entry->anti_replay_window_size), &tun, sa_index);
+}
+
 static void
 vl_api_ipsec_sad_entry_del_t_handler (vl_api_ipsec_sad_entry_del_t *mp)
 {
@@ -567,6 +617,19 @@ vl_api_ipsec_sad_entry_add_t_handler (vl_api_ipsec_sad_entry_add_t *mp)
                { rmp->stat_index = htonl (sa_index); });
 }
 
+static void
+vl_api_ipsec_sad_entry_add_v2_t_handler (vl_api_ipsec_sad_entry_add_v2_t *mp)
+{
+  vl_api_ipsec_sad_entry_add_reply_t *rmp;
+  u32 sa_index = ~0;
+  int rv;
+
+  rv = ipsec_sad_entry_add_v4 (&mp->entry, &sa_index);
+
+  REPLY_MACRO2 (VL_API_IPSEC_SAD_ENTRY_ADD_V2_REPLY,
+               { rmp->stat_index = htonl (sa_index); });
+}
+
 static void
 vl_api_ipsec_sad_entry_update_t_handler (vl_api_ipsec_sad_entry_update_t *mp)
 {
@@ -953,7 +1016,10 @@ send_ipsec_sa_details (ipsec_sa_t * sa, void *arg)
       mp->last_seq_inbound |= (u64) (clib_host_to_net_u32 (sa->seq_hi));
     }
   if (ipsec_sa_is_set_USE_ANTI_REPLAY (sa))
-    mp->replay_window = clib_host_to_net_u64 (sa->replay_window);
+    {
+      mp->replay_window =
+       clib_host_to_net_u64 (ipsec_sa_anti_replay_get_64b_window (sa));
+    }
 
   mp->stat_index = clib_host_to_net_u32 (sa->stat_index);
 
@@ -1040,7 +1106,10 @@ send_ipsec_sa_v2_details (ipsec_sa_t * sa, void *arg)
       mp->last_seq_inbound |= (u64) (clib_host_to_net_u32 (sa->seq_hi));
     }
   if (ipsec_sa_is_set_USE_ANTI_REPLAY (sa))
-    mp->replay_window = clib_host_to_net_u64 (sa->replay_window);
+    {
+      mp->replay_window =
+       clib_host_to_net_u64 (ipsec_sa_anti_replay_get_64b_window (sa));
+    }
 
   mp->stat_index = clib_host_to_net_u32 (sa->stat_index);
 
@@ -1120,7 +1189,10 @@ send_ipsec_sa_v3_details (ipsec_sa_t *sa, void *arg)
       mp->last_seq_inbound |= (u64) (clib_host_to_net_u32 (sa->seq_hi));
     }
   if (ipsec_sa_is_set_USE_ANTI_REPLAY (sa))
-    mp->replay_window = clib_host_to_net_u64 (sa->replay_window);
+    {
+      mp->replay_window =
+       clib_host_to_net_u64 (ipsec_sa_anti_replay_get_64b_window (sa));
+    }
 
   mp->stat_index = clib_host_to_net_u32 (sa->stat_index);
 
@@ -1200,7 +1272,10 @@ send_ipsec_sa_v4_details (ipsec_sa_t *sa, void *arg)
       mp->last_seq_inbound |= (u64) (clib_host_to_net_u32 (sa->seq_hi));
     }
   if (ipsec_sa_is_set_USE_ANTI_REPLAY (sa))
-    mp->replay_window = clib_host_to_net_u64 (sa->replay_window);
+    {
+      mp->replay_window =
+       clib_host_to_net_u64 (ipsec_sa_anti_replay_get_64b_window (sa));
+    }
 
   mp->thread_index = clib_host_to_net_u32 (sa->thread_index);
   mp->stat_index = clib_host_to_net_u32 (sa->stat_index);
@@ -1227,8 +1302,95 @@ vl_api_ipsec_sa_v4_dump_t_handler (vl_api_ipsec_sa_v4_dump_t *mp)
   ipsec_sa_walk (send_ipsec_sa_v4_details, &ctx);
 }
 
+static walk_rc_t
+send_ipsec_sa_v5_details (ipsec_sa_t *sa, void *arg)
+{
+  ipsec_dump_walk_ctx_t *ctx = arg;
+  vl_api_ipsec_sa_v5_details_t *mp;
+
+  mp = vl_msg_api_alloc (sizeof (*mp));
+  clib_memset (mp, 0, sizeof (*mp));
+  mp->_vl_msg_id = ntohs (REPLY_MSG_ID_BASE + VL_API_IPSEC_SA_V5_DETAILS);
+  mp->context = ctx->context;
+
+  mp->entry.sad_id = htonl (sa->id);
+  mp->entry.spi = htonl (sa->spi);
+  mp->entry.protocol = ipsec_proto_encode (sa->protocol);
+
+  mp->entry.crypto_algorithm = ipsec_crypto_algo_encode (sa->crypto_alg);
+  ipsec_key_encode (&sa->crypto_key, &mp->entry.crypto_key);
+
+  mp->entry.integrity_algorithm = ipsec_integ_algo_encode (sa->integ_alg);
+  ipsec_key_encode (&sa->integ_key, &mp->entry.integrity_key);
+
+  mp->entry.flags = ipsec_sad_flags_encode (sa);
+  mp->entry.salt = clib_host_to_net_u32 (sa->salt);
+
+  if (ipsec_sa_is_set_IS_PROTECT (sa))
+    {
+      ipsec_sa_dump_match_ctx_t ctx = {
+       .sai = sa - ipsec_sa_pool,
+       .sw_if_index = ~0,
+      };
+      ipsec_tun_protect_walk (ipsec_sa_dump_match_sa, &ctx);
+
+      mp->sw_if_index = htonl (ctx.sw_if_index);
+    }
+  else
+    mp->sw_if_index = ~0;
+
+  if (ipsec_sa_is_set_IS_TUNNEL (sa))
+    tunnel_encode (&sa->tunnel, &mp->entry.tunnel);
+
+  if (ipsec_sa_is_set_UDP_ENCAP (sa))
+    {
+      mp->entry.udp_src_port = sa->udp_hdr.src_port;
+      mp->entry.udp_dst_port = sa->udp_hdr.dst_port;
+    }
+
+  mp->seq_outbound = clib_host_to_net_u64 (((u64) sa->seq));
+  mp->last_seq_inbound = clib_host_to_net_u64 (((u64) sa->seq));
+  if (ipsec_sa_is_set_USE_ESN (sa))
+    {
+      mp->seq_outbound |= (u64) (clib_host_to_net_u32 (sa->seq_hi));
+      mp->last_seq_inbound |= (u64) (clib_host_to_net_u32 (sa->seq_hi));
+    }
+  if (ipsec_sa_is_set_USE_ANTI_REPLAY (sa))
+    {
+      mp->replay_window =
+       clib_host_to_net_u64 (ipsec_sa_anti_replay_get_64b_window (sa));
+
+      mp->entry.anti_replay_window_size =
+       clib_host_to_net_u32 (IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE (sa));
+    }
+
+  mp->thread_index = clib_host_to_net_u32 (sa->thread_index);
+  mp->stat_index = clib_host_to_net_u32 (sa->stat_index);
+
+  vl_api_send_msg (ctx->reg, (u8 *) mp);
+
+  return (WALK_CONTINUE);
+}
+
+static void
+vl_api_ipsec_sa_v5_dump_t_handler (vl_api_ipsec_sa_v5_dump_t *mp)
+{
+  vl_api_registration_t *reg;
+
+  reg = vl_api_client_index_to_registration (mp->client_index);
+  if (!reg)
+    return;
+
+  ipsec_dump_walk_ctx_t ctx = {
+    .reg = reg,
+    .context = mp->context,
+  };
+
+  ipsec_sa_walk (send_ipsec_sa_v5_details, &ctx);
+}
+
 static void
-vl_api_ipsec_backend_dump_t_handler (vl_api_ipsec_backend_dump_t * mp)
+vl_api_ipsec_backend_dump_t_handler (vl_api_ipsec_backend_dump_t *mp)
 {
   vl_api_registration_t *rp;
   ipsec_main_t *im = &ipsec_main;
index 6cb9aba..5aef630 100644 (file)
@@ -88,6 +88,7 @@ ipsec_sa_add_del_command_fn (vlib_main_t * vm,
   unformat_input_t _line_input, *line_input = &_line_input;
   ipsec_crypto_alg_t crypto_alg;
   ipsec_integ_alg_t integ_alg;
+  u32 anti_replay_window_size;
   ipsec_protocol_t proto;
   ipsec_sa_flags_t flags;
   clib_error_t *error;
@@ -105,6 +106,7 @@ ipsec_sa_add_del_command_fn (vlib_main_t * vm,
   is_add = 0;
   flags = IPSEC_SA_FLAG_NONE;
   proto = IPSEC_PROTOCOL_ESP;
+  anti_replay_window_size = 0;
   integ_alg = IPSEC_INTEG_ALG_NONE;
   crypto_alg = IPSEC_CRYPTO_ALG_NONE;
   udp_src = udp_dst = IPSEC_UDP_PORT_NONE;
@@ -153,6 +155,9 @@ ipsec_sa_add_del_command_fn (vlib_main_t * vm,
        udp_src = i;
       else if (unformat (line_input, "udp-dst-port %d", &i))
        udp_dst = i;
+      else if (unformat (line_input, "anti-replay-size %d",
+                        &anti_replay_window_size))
+       flags |= IPSEC_SA_FLAG_USE_ANTI_REPLAY;
       else if (unformat (line_input, "inbound"))
        flags |= IPSEC_SA_FLAG_IS_INBOUND;
       else if (unformat (line_input, "use-anti-replay"))
@@ -184,9 +189,10 @@ ipsec_sa_add_del_command_fn (vlib_main_t * vm,
          error = clib_error_return (0, "missing spi");
          goto done;
        }
-      rv = ipsec_sa_add_and_lock (id, spi, proto, crypto_alg, &ck, integ_alg,
-                                 &ik, flags, clib_host_to_net_u32 (salt),
-                                 udp_src, udp_dst, &tun, &sai);
+      rv =
+       ipsec_sa_add_and_lock (id, spi, proto, crypto_alg, &ck, integ_alg, &ik,
+                              flags, clib_host_to_net_u32 (salt), udp_src,
+                              udp_dst, anti_replay_window_size, &tun, &sai);
     }
   else
     {
index d1511ac..12381ce 100644 (file)
@@ -466,16 +466,18 @@ format_ipsec_sa (u8 * s, va_list * args)
   s = format (s, "\n   salt 0x%x", clib_net_to_host_u32 (sa->salt));
   s = format (s, "\n   thread-index:%d", sa->thread_index);
   s = format (s, "\n   seq %u seq-hi %u", sa->seq, sa->seq_hi);
-  s = format (s, "\n   window %U", format_ipsec_replay_window,
-             sa->replay_window);
-  s = format (s, "\n   crypto alg %U",
-             format_ipsec_crypto_alg, sa->crypto_alg);
+  s = format (s, "\n   window-size: %llu",
+             IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE (sa));
+  s = format (s, "\n   window: Bl <- %U Tl", format_ipsec_replay_window,
+             ipsec_sa_anti_replay_get_64b_window (sa));
+  s =
+    format (s, "\n   crypto alg %U", format_ipsec_crypto_alg, sa->crypto_alg);
   if (sa->crypto_alg && (flags & IPSEC_FORMAT_INSECURE))
     s = format (s, " key %U", format_ipsec_key, &sa->crypto_key);
   else
     s = format (s, " key [redacted]");
-  s = format (s, "\n   integrity alg %U",
-             format_ipsec_integ_alg, sa->integ_alg);
+  s =
+    format (s, "\n   integrity alg %U", format_ipsec_integ_alg, sa->integ_alg);
   if (sa->integ_alg && (flags & IPSEC_FORMAT_INSECURE))
     s = format (s, " key %U", format_ipsec_key, &sa->integ_key);
   else
index 98160cd..1656b9a 100644 (file)
@@ -329,7 +329,8 @@ 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)
+                      u16 dst_port, u32 anti_replay_window_size,
+                      const tunnel_t *tun, u32 *sa_out_index)
 {
   vlib_main_t *vm = vlib_get_main ();
   ipsec_main_t *im = &ipsec_main;
@@ -379,6 +380,9 @@ ipsec_sa_add_and_lock (u32 id, u32 spi, ipsec_protocol_t proto,
   ipsec_sa_set_crypto_alg (sa, crypto_alg);
   ipsec_sa_set_async_op_ids (sa);
 
+  if (ipsec_sa_is_set_USE_ANTI_REPLAY (sa) && anti_replay_window_size > 64)
+    ipsec_sa_set_ANTI_REPLAY_HUGE (sa);
+
   clib_memcpy (&sa->crypto_key, ck, sizeof (sa->crypto_key));
 
   sa->crypto_sync_key_index = vnet_crypto_key_add (
@@ -488,6 +492,18 @@ ipsec_sa_add_and_lock (u32 id, u32 spi, ipsec_protocol_t proto,
                                 !ipsec_sa_is_set_IS_TUNNEL_V6 (sa));
     }
 
+  /* window size rounded up to next power of 2 */
+  if (ipsec_sa_is_set_ANTI_REPLAY_HUGE (sa))
+    {
+      anti_replay_window_size = 1 << max_log2 (anti_replay_window_size);
+      sa->replay_window_huge =
+       clib_bitmap_set_region (0, 0, 1, anti_replay_window_size);
+    }
+  else
+    {
+      sa->replay_window = ~0;
+    }
+
   hash_set (im->sa_index_by_sa_id, sa->id, sa_index);
 
   if (sa_out_index)
@@ -525,6 +541,8 @@ ipsec_sa_del (ipsec_sa_t * sa)
   vnet_crypto_key_del (vm, sa->crypto_sync_key_index);
   if (sa->integ_alg != IPSEC_INTEG_ALG_NONE)
     vnet_crypto_key_del (vm, sa->integ_sync_key_index);
+  if (ipsec_sa_is_set_ANTI_REPLAY_HUGE (sa))
+    clib_bitmap_free (sa->replay_window_huge);
   pool_put (ipsec_sa_pool, sa);
 }
 
index cd90ef5..4705851 100644 (file)
@@ -121,7 +121,8 @@ typedef struct ipsec_key_t_
   _ (256, IS_CTR, "ctr")                                                      \
   _ (512, IS_ASYNC, "async")                                                  \
   _ (1024, NO_ALGO_NO_DROP, "no-algo-no-drop")                                \
-  _ (2048, IS_NULL_GMAC, "null-gmac")
+  _ (2048, IS_NULL_GMAC, "null-gmac")                                         \
+  _ (4096, ANTI_REPLAY_HUGE, "anti-replay-huge")
 
 typedef enum ipsec_sad_flags_t_
 {
@@ -167,7 +168,11 @@ typedef struct
 
   clib_pcg64i_random_t iv_prng;
 
-  u64 replay_window;
+  union
+  {
+    u64 replay_window;
+    clib_bitmap_t *replay_window_huge;
+  };
   dpo_id_t dpo;
 
   vnet_crypto_key_index_t crypto_key_index;
@@ -254,94 +259,149 @@ STATIC_ASSERT (STRUCT_OFFSET_OF (vnet_buffer_opaque_t, ipsec.sad_index) ==
                 STRUCT_OFFSET_OF (vnet_buffer_opaque_t, ip.save_protocol),
               "IPSec data is overlapping with IP data");
 
-#define _(a,v,s)                                                        \
-  always_inline int                                                     \
-  ipsec_sa_is_set_##v (const ipsec_sa_t *sa) {                          \
-    return (sa->flags & IPSEC_SA_FLAG_##v);                             \
+#define _(a, v, s)                                                            \
+  always_inline bool ipsec_sa_is_set_##v (const ipsec_sa_t *sa)               \
+  {                                                                           \
+    return (sa->flags & IPSEC_SA_FLAG_##v);                                   \
   }
 foreach_ipsec_sa_flags
 #undef _
-#define _(a,v,s)                                                        \
-  always_inline int                                                     \
-  ipsec_sa_set_##v (ipsec_sa_t *sa) {                                   \
-    return (sa->flags |= IPSEC_SA_FLAG_##v);                            \
+#define _(a, v, s)                                                            \
+  always_inline void ipsec_sa_set_##v (ipsec_sa_t *sa)                        \
+  {                                                                           \
+    sa->flags |= IPSEC_SA_FLAG_##v;                                           \
   }
   foreach_ipsec_sa_flags
 #undef _
-#define _(a,v,s)                                                        \
-  always_inline int                                                     \
-  ipsec_sa_unset_##v (ipsec_sa_t *sa) {                                 \
-    return (sa->flags &= ~IPSEC_SA_FLAG_##v);                           \
+#define _(a, v, s)                                                            \
+  always_inline int ipsec_sa_unset_##v (ipsec_sa_t *sa)                       \
+  {                                                                           \
+    return (sa->flags &= ~IPSEC_SA_FLAG_##v);                                 \
   }
-  foreach_ipsec_sa_flags
+    foreach_ipsec_sa_flags
 #undef _
-/**
- * @brief
- * SA packet & bytes counters
- */
-extern vlib_combined_counter_main_t ipsec_sa_counters;
+  /**
  * @brief
  * 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 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_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,
+  u32 anti_replay_window_size, 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);
 extern void ipsec_sa_lock (index_t sai);
 extern void ipsec_sa_clear (index_t sai);
-extern void ipsec_sa_set_crypto_alg (ipsec_sa_t * sa,
+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,
+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);
+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);
 
 extern u8 *format_ipsec_replay_window (u8 *s, va_list *args);
-extern u8 *format_ipsec_crypto_alg (u8 * s, va_list * args);
-extern u8 *format_ipsec_integ_alg (u8 * s, va_list * args);
-extern u8 *format_ipsec_sa (u8 * s, va_list * args);
-extern u8 *format_ipsec_key (u8 * s, va_list * args);
-extern uword unformat_ipsec_crypto_alg (unformat_input_t * input,
-                                       va_list * args);
-extern uword unformat_ipsec_integ_alg (unformat_input_t * input,
-                                      va_list * args);
-extern uword unformat_ipsec_key (unformat_input_t * input, va_list * args);
-
-#define IPSEC_UDP_PORT_NONE ((u16)~0)
+extern u8 *format_ipsec_crypto_alg (u8 *s, va_list *args);
+extern u8 *format_ipsec_integ_alg (u8 *s, va_list *args);
+extern u8 *format_ipsec_sa (u8 *s, va_list *args);
+extern u8 *format_ipsec_key (u8 *s, va_list *args);
+extern uword unformat_ipsec_crypto_alg (unformat_input_t *input,
+                                       va_list *args);
+extern uword unformat_ipsec_integ_alg (unformat_input_t *input, va_list *args);
+extern uword unformat_ipsec_key (unformat_input_t *input, va_list *args);
+
+#define IPSEC_UDP_PORT_NONE ((u16) ~0)
 
 /*
  * Anti Replay definitions
  */
 
-#define IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE (64)
-#define IPSEC_SA_ANTI_REPLAY_WINDOW_MAX_INDEX (IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE-1)
+#define IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE(_sa)                                 \
+  (u32) (PREDICT_FALSE (ipsec_sa_is_set_ANTI_REPLAY_HUGE (_sa)) ?             \
+                clib_bitmap_bytes (_sa->replay_window_huge) * 8 :                  \
+                BITS (_sa->replay_window))
+
+#define IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE_KNOWN_WIN(_sa, _is_huge)             \
+  (u32) (_is_huge ? clib_bitmap_bytes (_sa->replay_window_huge) * 8 :         \
+                         BITS (_sa->replay_window))
+
+#define IPSEC_SA_ANTI_REPLAY_WINDOW_N_SEEN(_sa)                               \
+  (u64) (PREDICT_FALSE (ipsec_sa_is_set_ANTI_REPLAY_HUGE (_sa)) ?             \
+                clib_bitmap_count_set_bits (_sa->replay_window_huge) :             \
+                count_set_bits (_sa->replay_window))
+
+#define IPSEC_SA_ANTI_REPLAY_WINDOW_N_SEEN_KNOWN_WIN(_sa, _is_huge)           \
+  (u64) (_is_huge ? clib_bitmap_count_set_bits (_sa->replay_window_huge) :    \
+                         count_set_bits (_sa->replay_window))
+
+#define IPSEC_SA_ANTI_REPLAY_WINDOW_MAX_INDEX(_sa)                            \
+  (u32) (IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE (_sa) - 1)
+
+#define IPSEC_SA_ANTI_REPLAY_WINDOW_MAX_INDEX_KNOWN_WIN(_sa, _is_huge)        \
+  (u32) (IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE (_sa, _is_huge) - 1)
 
 /*
  * sequence number less than the lower bound are outside of the window
  * From RFC4303 Appendix A:
  *  Bl = Tl - W + 1
  */
-#define IPSEC_SA_ANTI_REPLAY_WINDOW_LOWER_BOUND(_tl) (_tl - IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE + 1)
+#define IPSEC_SA_ANTI_REPLAY_WINDOW_LOWER_BOUND(_sa)                          \
+  (u32) (_sa->seq - IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE (_sa) + 1)
+
+#define IPSEC_SA_ANTI_REPLAY_WINDOW_LOWER_BOUND_KNOWN_WIN(_sa, _is_huge)      \
+  (u32) (_sa->seq -                                                           \
+        IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE_KNOWN_WIN (_sa, _is_huge) + 1)
+
+always_inline u64
+ipsec_sa_anti_replay_get_64b_window (const ipsec_sa_t *sa)
+{
+  if (!ipsec_sa_is_set_ANTI_REPLAY_HUGE (sa))
+    return sa->replay_window;
+
+  u64 w;
+  u32 window_size = IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE (sa);
+  u32 tl_win_index = sa->seq & (window_size - 1);
+
+  if (PREDICT_TRUE (tl_win_index >= 63))
+    return clib_bitmap_get_multiple (sa->replay_window_huge, tl_win_index - 63,
+                                    64);
+
+  w = clib_bitmap_get_multiple_no_check (sa->replay_window_huge, 0,
+                                        tl_win_index + 1)
+      << (63 - tl_win_index);
+  w |= clib_bitmap_get_multiple_no_check (sa->replay_window_huge,
+                                         window_size - 63 + tl_win_index,
+                                         63 - tl_win_index);
+
+  return w;
+}
 
 always_inline int
-ipsec_sa_anti_replay_check (const ipsec_sa_t *sa, u32 seq)
+ipsec_sa_anti_replay_check (const ipsec_sa_t *sa, u32 seq, bool ar_huge)
 {
-  if (ipsec_sa_is_set_USE_ANTI_REPLAY (sa) &&
-      sa->replay_window & (1ULL << (sa->seq - seq)))
-    return 1;
+  u32 window_size = IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE_KNOWN_WIN (sa, ar_huge);
+
+  /* we assume that the packet is in the window.
+   * if the packet falls left (sa->seq - seq >= window size),
+   * the result is wrong */
+
+  if (ar_huge)
+    return clib_bitmap_get (sa->replay_window_huge, seq & (window_size - 1));
   else
-    return 0;
+    return (sa->replay_window >> (window_size + seq - sa->seq - 1)) & 1;
+
+  return 0;
 }
 
 /*
@@ -361,10 +421,14 @@ ipsec_sa_anti_replay_check (const ipsec_sa_t *sa, u32 seq)
 always_inline int
 ipsec_sa_anti_replay_and_sn_advance (const ipsec_sa_t *sa, u32 seq,
                                     u32 hi_seq_used, bool post_decrypt,
-                                    u32 *hi_seq_req)
+                                    u32 *hi_seq_req, bool ar_huge)
 {
   ASSERT ((post_decrypt == false) == (hi_seq_req != 0));
 
+  u32 window_size = IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE_KNOWN_WIN (sa, ar_huge);
+  u32 window_lower_bound =
+    IPSEC_SA_ANTI_REPLAY_WINDOW_LOWER_BOUND_KNOWN_WIN (sa, ar_huge);
+
   if (!ipsec_sa_is_set_USE_ESN (sa))
     {
       if (hi_seq_req)
@@ -377,14 +441,11 @@ ipsec_sa_anti_replay_and_sn_advance (const ipsec_sa_t *sa, u32 seq,
       if (PREDICT_TRUE (seq > sa->seq))
        return 0;
 
-      u32 diff = sa->seq - seq;
-
-      if (IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE > diff)
-       return ((sa->replay_window & (1ULL << diff)) ? 1 : 0);
-      else
+      /* does the packet fall out on the left of the window */
+      if (sa->seq >= seq + window_size)
        return 1;
 
-      return 0;
+      return ipsec_sa_anti_replay_check (sa, seq, ar_huge);
     }
 
   if (!ipsec_sa_is_set_USE_ANTI_REPLAY (sa))
@@ -424,14 +485,15 @@ ipsec_sa_anti_replay_and_sn_advance (const ipsec_sa_t *sa, u32 seq,
        */
       return 0;
     }
-  if (PREDICT_TRUE (sa->seq >= (IPSEC_SA_ANTI_REPLAY_WINDOW_MAX_INDEX)))
+
+  if (PREDICT_TRUE (sa->seq >= window_size - 1))
     {
       /*
-       * the last sequence number VPP recieved is more than one
+       * the last sequence number VPP received is more than one
        * window size greater than zero.
        * Case A from RFC4303 Appendix A.
        */
-      if (seq < IPSEC_SA_ANTI_REPLAY_WINDOW_LOWER_BOUND (sa->seq))
+      if (seq < window_lower_bound)
        {
          /*
           * the received sequence number is lower than the lower bound
@@ -443,7 +505,7 @@ ipsec_sa_anti_replay_and_sn_advance (const ipsec_sa_t *sa, u32 seq,
            {
              if (hi_seq_used == sa->seq_hi)
                /* the high sequence number used to succesfully decrypt this
-                * packet is the same as the last-sequnence number of the SA.
+                * packet is the same as the last-sequence number of the SA.
                 * that means this packet did not cause a wrap.
                 * this packet is thus out of window and should be dropped */
                return 1;
@@ -455,8 +517,8 @@ ipsec_sa_anti_replay_and_sn_advance (const ipsec_sa_t *sa, u32 seq,
            }
          else
            {
-             /* pre-decrypt it might be the might that casues a wrap, we
-              * need to decrpyt to find out */
+             /* pre-decrypt it might be the packet that causes a wrap, we
+              * need to decrypt it to find out */
              if (hi_seq_req)
                *hi_seq_req = sa->seq_hi + 1;
              return 0;
@@ -465,17 +527,17 @@ ipsec_sa_anti_replay_and_sn_advance (const ipsec_sa_t *sa, u32 seq,
       else
        {
          /*
-          * the recieved sequence number greater than the low
+          * the received sequence number greater than the low
           * end of the window.
           */
          if (hi_seq_req)
            *hi_seq_req = sa->seq_hi;
          if (seq <= sa->seq)
            /*
-            * The recieved seq number is within bounds of the window
+            * The received seq number is within bounds of the window
             * check if it's a duplicate
             */
-           return (ipsec_sa_anti_replay_check (sa, seq));
+           return ipsec_sa_anti_replay_check (sa, seq, ar_huge);
          else
            /*
             * The received sequence number is greater than the window
@@ -488,14 +550,14 @@ ipsec_sa_anti_replay_and_sn_advance (const ipsec_sa_t *sa, u32 seq,
   else
     {
       /*
-       * the last sequence number VPP recieved is within one window
+       * the last sequence number VPP received is within one window
        * size of zero, i.e. 0 < TL < WINDOW_SIZE, the lower bound is thus a
        * large sequence number.
-       * Note that the check below uses unsiged integer arthimetic, so the
+       * Note that the check below uses unsigned integer arithmetic, so the
        * RHS will be a larger number.
        * Case B from RFC4303 Appendix A.
        */
-      if (seq < IPSEC_SA_ANTI_REPLAY_WINDOW_LOWER_BOUND (sa->seq))
+      if (seq < window_lower_bound)
        {
          /*
           * the sequence number is less than the lower bound.
@@ -508,7 +570,7 @@ ipsec_sa_anti_replay_and_sn_advance (const ipsec_sa_t *sa, u32 seq,
               */
              if (hi_seq_req)
                *hi_seq_req = sa->seq_hi;
-             return (ipsec_sa_anti_replay_check (sa, seq));
+             return ipsec_sa_anti_replay_check (sa, seq, ar_huge);
            }
          else
            {
@@ -516,7 +578,7 @@ ipsec_sa_anti_replay_and_sn_advance (const ipsec_sa_t *sa, u32 seq,
               * the packet is less the window lower bound or greater than
               * the higher bound, depending on how you look at it...
               * We're assuming, given that the last sequence number received,
-              * TL < WINDOW_SIZE, that a largeer seq num is more likely to be
+              * TL < WINDOW_SIZE, that a larger seq num is more likely to be
               * a packet that moves the window forward, than a packet that has
               * wrapped the high sequence again. If it were the latter then
               * we've lost close to 2^32 packets.
@@ -529,15 +591,14 @@ ipsec_sa_anti_replay_and_sn_advance (const ipsec_sa_t *sa, u32 seq,
       else
        {
          /*
-          * the packet seq number is between the lower bound (a large nubmer)
-          * and MAX_SEQ_NUM. This is in the window since the window upper bound
-          * tl > 0.
-          * However, since TL is the other side of 0 to the received
-          * packet, the SA has moved on to a higher sequence number.
+          * the packet seq number is between the lower bound (a large number)
+          * and MAX_SEQ_NUM. This is in the window since the window upper
+          * bound tl > 0. However, since TL is the other side of 0 to the
+          * received packet, the SA has moved on to a higher sequence number.
           */
          if (hi_seq_req)
            *hi_seq_req = sa->seq_hi - 1;
-         return (ipsec_sa_anti_replay_check (sa, seq));
+         return ipsec_sa_anti_replay_check (sa, seq, ar_huge);
        }
     }
 
@@ -547,45 +608,148 @@ ipsec_sa_anti_replay_and_sn_advance (const ipsec_sa_t *sa, u32 seq,
 }
 
 always_inline u32
-ipsec_sa_anti_replay_window_shift (ipsec_sa_t *sa, u32 inc)
+ipsec_sa_anti_replay_window_shift (ipsec_sa_t *sa, u32 inc, bool ar_huge)
 {
   u32 n_lost = 0;
+  u32 seen = 0;
+  u32 window_size = IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE_KNOWN_WIN (sa, ar_huge);
 
-  if (inc < IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE)
+  if (inc < window_size)
     {
-      if (sa->seq > IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE)
+      if (ar_huge)
+       {
+         /* the number of packets we saw in this section of the window */
+         clib_bitmap_t *window = sa->replay_window_huge;
+         u32 window_lower_bound = (sa->seq + 1) & (window_size - 1);
+         u32 window_next_lower_bound =
+           (window_lower_bound + inc) & (window_size - 1);
+
+         uword i_block, i_word_start, i_word_end, full_words;
+         uword n_blocks = window_size >> log2_uword_bits;
+         uword mask;
+
+         i_block = window_lower_bound >> log2_uword_bits;
+
+         i_word_start = window_lower_bound & (uword_bits - 1);
+         i_word_end = window_next_lower_bound & (uword_bits - 1);
+
+         /* We stay in the same word */
+         if (i_word_start + inc <= uword_bits)
+           {
+             mask = pow2_mask (inc) << i_word_start;
+             seen += count_set_bits (window[i_block] & mask);
+             window[i_block] &= ~mask;
+           }
+         else
+           {
+             full_words = (inc + i_word_start - uword_bits - i_word_end) >>
+                          log2_uword_bits;
+
+             /* count set bits in the first word */
+             mask = (uword) ~0 << i_word_start;
+             seen += count_set_bits (window[i_block] & mask);
+             window[i_block] &= ~mask;
+             i_block = (i_block + 1) & (n_blocks - 1);
+
+             /* count set bits in the next full words */
+             /* even if the last word need to be fully counted, we treat it
+              * apart */
+             while (full_words >= 8)
+               {
+                 if (full_words >= 16)
+                   {
+                     /* prefect the next 8 blocks (64 bytes) */
+                     clib_prefetch_store (
+                       &window[(i_block + 8) & (n_blocks - 1)]);
+                   }
+
+                 seen += count_set_bits (window[i_block]);
+                 seen +=
+                   count_set_bits (window[(i_block + 1) & (n_blocks - 1)]);
+                 seen +=
+                   count_set_bits (window[(i_block + 2) & (n_blocks - 1)]);
+                 seen +=
+                   count_set_bits (window[(i_block + 3) & (n_blocks - 1)]);
+                 seen +=
+                   count_set_bits (window[(i_block + 4) & (n_blocks - 1)]);
+                 seen +=
+                   count_set_bits (window[(i_block + 5) & (n_blocks - 1)]);
+                 seen +=
+                   count_set_bits (window[(i_block + 6) & (n_blocks - 1)]);
+                 seen +=
+                   count_set_bits (window[(i_block + 7) & (n_blocks - 1)]);
+                 window[i_block] = 0;
+                 window[(i_block + 1) & (n_blocks - 1)] = 0;
+                 window[(i_block + 2) & (n_blocks - 1)] = 0;
+                 window[(i_block + 3) & (n_blocks - 1)] = 0;
+                 window[(i_block + 4) & (n_blocks - 1)] = 0;
+                 window[(i_block + 5) & (n_blocks - 1)] = 0;
+                 window[(i_block + 6) & (n_blocks - 1)] = 0;
+                 window[(i_block + 7) & (n_blocks - 1)] = 0;
+
+                 i_block = (i_block + 8) & (n_blocks - 1);
+                 full_words -= 8;
+               }
+             while (full_words > 0)
+               {
+                 // last word is treated after the loop
+                 seen += count_set_bits (window[i_block]);
+                 window[i_block] = 0;
+                 i_block = (i_block + 1) & (n_blocks - 1);
+                 full_words--;
+               }
+
+             /* the last word */
+             mask = pow2_mask (i_word_end);
+             seen += count_set_bits (window[i_block] & mask);
+             window[i_block] &= ~mask;
+           }
+
+         clib_bitmap_set_no_check (window,
+                                   (sa->seq + inc) & (window_size - 1), 1);
+       }
+      else
        {
          /*
           * 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;
+         u64 old = sa->replay_window & pow2_mask (inc);
          /* 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;
+         seen = count_set_bits (old);
+         sa->replay_window =
+           ((sa->replay_window) >> inc) | (1ULL << (window_size - 1));
        }
-      sa->replay_window = ((sa->replay_window) << inc) | 1;
+
+      /*
+       * the number we missed is the size of the window section
+       * minus the number we saw.
+       */
+      n_lost = inc - seen;
     }
   else
     {
       /* holes in the replay window are lost packets */
-      n_lost = BITS (u64) - count_set_bits (sa->replay_window);
+      n_lost = window_size - IPSEC_SA_ANTI_REPLAY_WINDOW_N_SEEN (sa);
 
       /* any sequence numbers that now fall outside the window
        * are forever lost */
-      n_lost += inc - IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE;
+      n_lost += inc - window_size;
 
-      sa->replay_window = 1;
+      if (PREDICT_FALSE (ipsec_sa_is_set_ANTI_REPLAY_HUGE (sa)))
+       {
+         clib_bitmap_zero (sa->replay_window_huge);
+         clib_bitmap_set_no_check (sa->replay_window_huge,
+                                   (sa->seq + inc) & (window_size - 1), 1);
+       }
+      else
+       {
+         sa->replay_window = 1ULL << (window_size - 1);
+       }
     }
 
-  return (n_lost);
+  return n_lost;
 }
 
 /*
@@ -599,9 +763,10 @@ ipsec_sa_anti_replay_window_shift (ipsec_sa_t *sa, u32 inc)
  */
 always_inline u64
 ipsec_sa_anti_replay_advance (ipsec_sa_t *sa, u32 thread_index, u32 seq,
-                             u32 hi_seq)
+                             u32 hi_seq, bool ar_huge)
 {
   u64 n_lost = 0;
+  u32 window_size = IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE_KNOWN_WIN (sa, ar_huge);
   u32 pos;
 
   if (ipsec_sa_is_set_USE_ESN (sa))
@@ -611,25 +776,33 @@ ipsec_sa_anti_replay_advance (ipsec_sa_t *sa, u32 thread_index, u32 seq,
       if (wrap == 0 && seq > sa->seq)
        {
          pos = seq - sa->seq;
-         n_lost = ipsec_sa_anti_replay_window_shift (sa, pos);
+         n_lost = ipsec_sa_anti_replay_window_shift (sa, pos, ar_huge);
          sa->seq = seq;
        }
       else if (wrap > 0)
        {
-         pos = ~seq + sa->seq + 1;
-         n_lost = ipsec_sa_anti_replay_window_shift (sa, pos);
+         pos = seq + ~sa->seq + 1;
+         n_lost = ipsec_sa_anti_replay_window_shift (sa, pos, ar_huge);
          sa->seq = seq;
          sa->seq_hi = hi_seq;
        }
       else if (wrap < 0)
        {
          pos = ~seq + sa->seq + 1;
-         sa->replay_window |= (1ULL << pos);
+         if (ar_huge)
+           clib_bitmap_set_no_check (sa->replay_window_huge,
+                                     seq & (window_size - 1), 1);
+         else
+           sa->replay_window |= (1ULL << (window_size - 1 - pos));
        }
       else
        {
          pos = sa->seq - seq;
-         sa->replay_window |= (1ULL << pos);
+         if (ar_huge)
+           clib_bitmap_set_no_check (sa->replay_window_huge,
+                                     seq & (window_size - 1), 1);
+         else
+           sa->replay_window |= (1ULL << (window_size - 1 - pos));
        }
     }
   else
@@ -637,13 +810,17 @@ ipsec_sa_anti_replay_advance (ipsec_sa_t *sa, u32 thread_index, u32 seq,
       if (seq > sa->seq)
        {
          pos = seq - sa->seq;
-         n_lost = ipsec_sa_anti_replay_window_shift (sa, pos);
+         n_lost = ipsec_sa_anti_replay_window_shift (sa, pos, ar_huge);
          sa->seq = seq;
        }
       else
        {
          pos = sa->seq - seq;
-         sa->replay_window |= (1ULL << pos);
+         if (ar_huge)
+           clib_bitmap_set_no_check (sa->replay_window_huge,
+                                     seq & (window_size - 1), 1);
+         else
+           sa->replay_window |= (1ULL << (window_size - 1 - pos));
        }
     }
 
index 9b42ae7..86d09f1 100644 (file)
@@ -282,6 +282,12 @@ vl_api_ipsec_sad_entry_add_reply_t_handler (
 {
 }
 
+static void
+vl_api_ipsec_sad_entry_add_v2_reply_t_handler (
+  vl_api_ipsec_sad_entry_add_reply_t *mp)
+{
+}
+
 static int
 api_ipsec_sad_entry_del (vat_main_t *vat)
 {
@@ -341,6 +347,18 @@ api_ipsec_sa_v3_dump (vat_main_t *vat)
   return -1;
 }
 
+static int
+api_ipsec_sa_v4_dump (vat_main_t *vat)
+{
+  return -1;
+}
+
+static int
+api_ipsec_sa_v5_dump (vat_main_t *vat)
+{
+  return -1;
+}
+
 static int
 api_ipsec_tunnel_protect_dump (vat_main_t *vat)
 {
@@ -365,6 +383,12 @@ api_ipsec_sad_entry_add (vat_main_t *vat)
   return -1;
 }
 
+static int
+api_ipsec_sad_entry_add_v2 (vat_main_t *vat)
+{
+  return -1;
+}
+
 static void
 vl_api_ipsec_spd_entry_add_del_reply_t_handler (
   vl_api_ipsec_spd_entry_add_del_reply_t *mp)
@@ -394,6 +418,16 @@ vl_api_ipsec_sa_v3_details_t_handler (vl_api_ipsec_sa_v3_details_t *mp)
 {
 }
 
+static void
+vl_api_ipsec_sa_v4_details_t_handler (vl_api_ipsec_sa_v4_details_t *mp)
+{
+}
+
+static void
+vl_api_ipsec_sa_v5_details_t_handler (vl_api_ipsec_sa_v5_details_t *mp)
+{
+}
+
 static int
 api_ipsec_spd_interface_dump (vat_main_t *vat)
 {
@@ -445,17 +479,6 @@ api_ipsec_sa_dump (vat_main_t *vam)
   return ret;
 }
 
-static void
-vl_api_ipsec_sa_v4_details_t_handler (vl_api_ipsec_sa_v4_details_t *mp)
-{
-}
-
-static int
-api_ipsec_sa_v4_dump (vat_main_t *mp)
-{
-  return -1;
-}
-
 static void
 vl_api_ipsec_sa_details_t_handler (vl_api_ipsec_sa_details_t *mp)
 {
index 9adcc6a..37c1141 100644 (file)
@@ -196,9 +196,6 @@ typedef ipsec_spd_entry_v2
 
 
 /** \brief IPsec: Security Association Database entry
-    @param client_index - opaque cookie to identify the sender
-    @param context - sender context, to match reply w/ request
-    @param is_add - add SAD entry if non-zero, else delete
     @param sad_id - sad id
     @param spi - security parameter index
     @param protocol - 0 = AH, 1 = ESP
@@ -206,6 +203,7 @@ typedef ipsec_spd_entry_v2
     @param crypto_key - crypto keying material
     @param integrity_algorithm - one of the supported algorithms
     @param integrity_key - integrity keying material
+    @param flags - SA flags (see ipsec_sad_flags above)
     @param tunnel_src_address - IPsec tunnel source address IPv6 if is_tunnel_ipv6 is non-zero, else IPv4. Only valid if is_tunnel is non-zero
     @param tunnel_dst_address - IPsec tunnel destination address IPv6 if is_tunnel_ipv6 is non-zero, else IPv4. Only valid if is_tunnel is non-zero
     @param tx_table_id - the FIB id used for encapsulated packets
@@ -290,6 +288,46 @@ typedef ipsec_sad_entry_v3
   u16 udp_dst_port [default=4500];
 };
 
+/** \brief IPsec: Security Association Database entry
+    @param sad_id - sad id
+    @param spi - security parameter index
+    @param protocol - 0 = AH, 1 = ESP
+    @param crypto_algorithm - a supported crypto algorithm
+    @param crypto_key - crypto keying material
+    @param integrity_algorithm - one of the supported algorithms
+    @param integrity_key - integrity keying material
+    @param flags - SA flags (see ipsec_sad_flags above)
+    @param tunnel - tunnel description (see vnet/tunnel/tunnel_types.api)
+    @param salt - for use with counter mode ciphers
+    @param udp_src_port - If using UDP Encapsulation, use this source port for
+                          TX. It is ignored for RX.
+    @param udp_dst_port - If using UDP Encapsulation, use this destination port
+                          for TX. Expect traffic on this port for RX.
+    @param anti_replay_window_size - AR window size to use. The supplied value is round up to the nearest power of 2.
+ */
+typedef ipsec_sad_entry_v4
+{
+  u32 sad_id;
+  u32 spi;
+
+  vl_api_ipsec_proto_t protocol;
+
+  vl_api_ipsec_crypto_alg_t crypto_algorithm;
+  vl_api_key_t crypto_key;
+
+  vl_api_ipsec_integ_alg_t integrity_algorithm;
+  vl_api_key_t integrity_key;
+
+  vl_api_ipsec_sad_flags_t flags;
+
+  vl_api_tunnel_t tunnel;
+
+  u32 salt;
+  u16 udp_src_port [default=4500];
+  u16 udp_dst_port [default=4500];
+
+  u32 anti_replay_window_size [default=64];
+};
 
 /*
  * Local Variables:
index abb1405..99d6595 100644 (file)
@@ -245,7 +245,7 @@ clib_bitmap_get_multiple_no_check (uword * ai, uword i, uword n_bits)
   uword i0 = i / BITS (ai[0]);
   uword i1 = i % BITS (ai[0]);
   ASSERT (i1 + n_bits <= BITS (uword));
-  return 0 != ((ai[i0] >> i1) & pow2_mask (n_bits));
+  return ((ai[i0] >> i1) & pow2_mask (n_bits));
 }
 
 /** Gets the ith through ith + n_bits bit values from a bitmap
index 5d83510..c438fbb 100644 (file)
@@ -51,6 +51,8 @@ class IPsecIPv4Params:
         self.outer_flow_label = 0
         self.inner_flow_label = 0x12345
 
+        self.anti_replay_window_size = 64
+
         self.auth_algo_vpp_id = (
             VppEnum.vl_api_ipsec_integ_alg_t.IPSEC_API_INTEG_ALG_SHA1_96
         )
@@ -98,6 +100,8 @@ class IPsecIPv6Params:
         self.outer_flow_label = 0
         self.inner_flow_label = 0x12345
 
+        self.anti_replay_window_size = 64
+
         self.auth_algo_vpp_id = (
             VppEnum.vl_api_ipsec_integ_alg_t.IPSEC_API_INTEG_ALG_SHA1_96
         )
@@ -625,22 +629,34 @@ class IpsecTra4(object):
     def verify_tra_anti_replay(self):
         p = self.params[socket.AF_INET]
         esn_en = p.vpp_tra_sa.esn_en
+        anti_replay_window_size = p.anti_replay_window_size
 
         seq_cycle_node_name = "/err/%s/seq_cycled" % self.tra4_encrypt_node_name
         replay_count = self.get_replay_counts(p)
+        initial_sa_node_replay_diff = replay_count - p.tra_sa_in.get_err("replay")
         hash_failed_count = self.get_hash_failed_counts(p)
         seq_cycle_count = self.statistics.get_err_counter(seq_cycle_node_name)
+        initial_sa_node_cycled_diff = seq_cycle_count - p.tra_sa_in.get_err(
+            "seq_cycled"
+        )
         hash_err = "integ_error"
 
         if ESP == self.encryption_type:
             undersize_node_name = "/err/%s/runt" % self.tra4_decrypt_node_name[0]
             undersize_count = self.statistics.get_err_counter(undersize_node_name)
+            initial_sa_node_undersize_diff = undersize_count - p.tra_sa_in.get_err(
+                "runt"
+            )
             # For AES-GCM an error in the hash is reported as a decryption failure
             if p.crypt_algo in ("AES-GCM", "AES-NULL-GMAC"):
                 hash_err = "decryption_failed"
         # In async mode, we don't report errors in the hash.
         if p.async_mode:
             hash_err = ""
+        else:
+            initial_sa_node_hash_diff = hash_failed_count - p.tra_sa_in.get_err(
+                hash_err
+            )
 
         #
         # send packets with seq numbers 1->34
@@ -666,7 +682,7 @@ class IpsecTra4(object):
         self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2)
         replay_count += len(pkts)
         self.assertEqual(self.get_replay_counts(p), replay_count)
-        err = p.tra_sa_in.get_err("replay")
+        err = p.tra_sa_in.get_err("replay") + initial_sa_node_replay_diff
         self.assertEqual(err, replay_count)
 
         #
@@ -684,29 +700,31 @@ class IpsecTra4(object):
         recv_pkts = self.send_and_expect(self.tra_if, pkts * 8, self.tra_if, n_rx=1)
         replay_count += 7
         self.assertEqual(self.get_replay_counts(p), replay_count)
-        err = p.tra_sa_in.get_err("replay")
+        err = p.tra_sa_in.get_err("replay") + initial_sa_node_replay_diff
         self.assertEqual(err, replay_count)
 
         #
-        # now move the window over to 257 (more than one byte) and into Case A
+        # now move the window over to anti_replay_window_size + 100 and into Case A
         #
         self.vapi.cli("clear error")
         pkt = Ether(
             src=self.tra_if.remote_mac, dst=self.tra_if.local_mac
         ) / p.scapy_tra_sa.encrypt(
             IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
-            seq_num=257,
+            seq_num=anti_replay_window_size + 100,
         )
         recv_pkts = self.send_and_expect(self.tra_if, [pkt], self.tra_if)
 
+        self.logger.info(self.vapi.ppcli("show ipsec sa 1"))
+
         # replayed packets are dropped
         self.send_and_assert_no_replies(self.tra_if, pkt * 3, timeout=0.2)
         replay_count += 3
         self.assertEqual(self.get_replay_counts(p), replay_count)
-        err = p.tra_sa_in.get_err("replay")
+        err = p.tra_sa_in.get_err("replay") + initial_sa_node_replay_diff
         self.assertEqual(err, replay_count)
 
-        # the window size is 64 packets
+        # the window size is anti_replay_window_size packets
         # in window are still accepted
         pkt = Ether(
             src=self.tra_if.remote_mac, dst=self.tra_if.local_mac
@@ -714,7 +732,6 @@ class IpsecTra4(object):
             IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
             seq_num=200,
         )
-        recv_pkts = self.send_and_expect(self.tra_if, [pkt], self.tra_if)
 
         # a packet that does not decrypt does not move the window forward
         bogus_sa = SecurityAssociation(
@@ -729,14 +746,14 @@ class IpsecTra4(object):
             src=self.tra_if.remote_mac, dst=self.tra_if.local_mac
         ) / bogus_sa.encrypt(
             IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
-            seq_num=350,
+            seq_num=anti_replay_window_size + 200,
         )
         self.send_and_assert_no_replies(self.tra_if, pkt * 17, timeout=0.2)
 
         hash_failed_count += 17
         self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count)
         if hash_err != "":
-            err = p.tra_sa_in.get_err(hash_err)
+            err = p.tra_sa_in.get_err(hash_err) + initial_sa_node_hash_diff
             self.assertEqual(err, hash_failed_count)
 
         # a malformed 'runt' packet
@@ -747,13 +764,13 @@ class IpsecTra4(object):
                 src=self.tra_if.remote_mac, dst=self.tra_if.local_mac
             ) / bogus_sa.encrypt(
                 IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
-                seq_num=350,
+                seq_num=anti_replay_window_size + 200,
             )
             self.send_and_assert_no_replies(self.tra_if, pkt * 17, timeout=0.2)
 
             undersize_count += 17
             self.assert_error_counter_equal(undersize_node_name, undersize_count)
-            err = p.tra_sa_in.get_err("runt")
+            err = p.tra_sa_in.get_err("runt") + initial_sa_node_undersize_diff
             self.assertEqual(err, undersize_count)
 
         # which we can determine since this packet is still in the window
@@ -784,21 +801,21 @@ class IpsecTra4(object):
             hash_failed_count += 17
             self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count)
             if hash_err != "":
-                err = p.tra_sa_in.get_err(hash_err)
+                err = p.tra_sa_in.get_err(hash_err) + initial_sa_node_hash_diff
                 self.assertEqual(err, hash_failed_count)
 
         else:
             replay_count += 17
             self.assertEqual(self.get_replay_counts(p), replay_count)
-            err = p.tra_sa_in.get_err("replay")
+            err = p.tra_sa_in.get_err("replay") + initial_sa_node_replay_diff
             self.assertEqual(err, replay_count)
 
-        # valid packet moves the window over to 258
+        # valid packet moves the window over to anti_replay_window_size + 258
         pkt = Ether(
             src=self.tra_if.remote_mac, dst=self.tra_if.local_mac
         ) / p.scapy_tra_sa.encrypt(
             IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
-            seq_num=258,
+            seq_num=anti_replay_window_size + 258,
         )
         rx = self.send_and_expect(self.tra_if, [pkt], self.tra_if)
         decrypted = p.vpp_tra_sa.decrypt(rx[0][IP])
@@ -852,7 +869,7 @@ class IpsecTra4(object):
             decrypted = p.vpp_tra_sa.decrypt(rx[0][IP])
 
             #
-            # A packet that has seq num between (2^32-64) and 5 is within
+            # A packet that has seq num between (2^32-anti_replay_window_size)+4 and 5 is within
             # the window
             #
             p.scapy_tra_sa.seq_num = 0xFFFFFFFD
@@ -883,19 +900,19 @@ class IpsecTra4(object):
             hash_failed_count += 1
             self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count)
             if hash_err != "":
-                err = p.tra_sa_in.get_err(hash_err)
+                err = p.tra_sa_in.get_err(hash_err) + initial_sa_node_hash_diff
                 self.assertEqual(err, hash_failed_count)
 
             #
             # but if we move the window forward to case B, then we can wrap
             # again
             #
-            p.scapy_tra_sa.seq_num = 0x100000555
+            p.scapy_tra_sa.seq_num = 0x100000000 + anti_replay_window_size + 0x555
             pkt = Ether(
                 src=self.tra_if.remote_mac, dst=self.tra_if.local_mac
             ) / p.scapy_tra_sa.encrypt(
                 IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
-                seq_num=0x100000555,
+                seq_num=p.scapy_tra_sa.seq_num,
             )
             rx = self.send_and_expect(self.tra_if, [pkt], self.tra_if)
             decrypted = p.vpp_tra_sa.decrypt(rx[0][IP])
@@ -918,7 +935,7 @@ class IpsecTra4(object):
             self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2)
             seq_cycle_count += len(pkts)
             self.assert_error_counter_equal(seq_cycle_node_name, seq_cycle_count)
-            err = p.tra_sa_out.get_err("seq_cycled")
+            err = p.tra_sa_out.get_err("seq_cycled") + initial_sa_node_cycled_diff
             self.assertEqual(err, seq_cycle_count)
 
         # move the security-associations seq number on to the last we used
@@ -1068,6 +1085,768 @@ class IpsecTra4(object):
         self.assert_packet_counter_equal(self.tra4_encrypt_node_name, count)
         self.assert_packet_counter_equal(self.tra4_decrypt_node_name[0], count)
 
+    def _verify_tra_anti_replay_algorithm_esn(self):
+        def seq_num(seqh, seql):
+            return (seqh << 32) | (seql & 0xFFFF_FFFF)
+
+        p = self.params[socket.AF_INET]
+        anti_replay_window_size = p.anti_replay_window_size
+
+        seq_cycle_node_name = "/err/%s/seq_cycled" % self.tra4_encrypt_node_name
+        replay_count = self.get_replay_counts(p)
+        hash_failed_count = self.get_hash_failed_counts(p)
+        seq_cycle_count = self.statistics.get_err_counter(seq_cycle_node_name)
+
+        if ESP == self.encryption_type:
+            undersize_node_name = "/err/%s/runt" % self.tra4_decrypt_node_name[0]
+            undersize_count = self.statistics.get_err_counter(undersize_node_name)
+
+        # reset the TX SA to avoid conflict with left configuration
+        self.vapi.cli(f"test ipsec sa {p.vpp_tra_sa_id} seq 0x0")
+
+        """
+        RFC 4303 Appendix A2. Case A
+
+        |: new Th marker
+        a-i: possible seq num received
+        +: Bl, Tl, Bl', Tl'
+        [BT]l(sign) = [BT]l (sign) 2^32 mod 2^32 (Th inc/dec-remented by 1)
+
+                Th - 1               Th                Th + 1
+        --|--a--+---b---+-c--|--d--+---e---+-f--|--g--+---h---+--i-|--
+                =========          =========          =========
+                Bl-     Tl-        Bl      Tl         Bl+     Tl+
+
+        Case A implies Tl >= W - 1
+        """
+
+        Th = 1
+        Tl = anti_replay_window_size + 40
+        Bl = Tl - anti_replay_window_size + 1
+
+        # move VPP's RX AR window to Case A
+        self.vapi.cli(f"test ipsec sa {p.scapy_tra_sa_id} seq {seq_num(Th, Tl):#x}")
+        p.scapy_tra_sa.seq_num = seq_num(Th, Tl)
+
+        """
+        case a: Seql < Bl
+            - pre-crypto check: algorithm predicts that the packet wrap the window
+                -> Seqh = Th + 1
+            - integrity check: should fail
+            - post-crypto check: ...
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th - 1, Bl - 20), seq_num(Th - 1, Bl - 5))
+        ]
+
+        # out-of-window packets fail integrity check
+        self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2)
+        hash_failed_count += len(pkts)
+        self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count)
+
+        """
+            case b: Bl <= Seql <= Tl
+                - pre-crypto check: algorithm predicts that the packet is in the window
+                    -> Seqh = Th
+                    -> check for a replayed packet with Seql
+                - integrity check: should fail
+                - post-crypto check: ...
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th, Tl - 10), seq_num(Th, Tl - 5))
+        ]
+        self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        p.scapy_tra_sa.seq_num = seq_num(Th - 1, Tl)
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th - 1, Tl - 35), seq_num(Th - 1, Tl - 5))
+        ]
+
+        self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2)
+
+        # some packets are rejected by the pre-crypto check
+        replay_count += 5
+        self.assertEqual(self.get_replay_counts(p), replay_count)
+
+        # out-of-window packets fail integrity check
+        hash_failed_count += len(pkts) - 5
+        self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count)
+
+        """
+            case c: Seql > Tl
+                - pre-crypto check: algorithm predicts that the packet does not wrap the window
+                    -> Seqh = Th
+                - integrity check: should fail
+                - post-crypto check: ...
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th - 1, Tl + 5), seq_num(Th - 1, Tl + 20))
+        ]
+
+        self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2)
+
+        # out-of-window packets fail integrity check
+        hash_failed_count += len(pkts)
+        self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count)
+
+        """
+            case d: Seql < Bl
+                - pre-crypto check: algorithm predicts that the packet wrap the window
+                    -> Seqh = Th + 1
+                - integrity check: should fail
+                - post-crypto check: ...
+        """
+        p.scapy_tra_sa.seq_num = seq_num(Th, Tl)
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th, Bl - 20), seq_num(Th, Bl - 5))
+        ]
+
+        # out-of-window packets fail integrity check
+        self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2)
+        hash_failed_count += len(pkts)
+        self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count)
+
+        """
+            case e: Bl <= Seql <= Tl
+                - pre-crypto check: algorithm predicts that the packet is in the window
+                    -> Seqh = Th
+                    -> check for a replayed packet with Seql
+                - integrity check: should pass
+                - post-crypto check: should pass
+                    -> Seql is marked in the AR window
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th, Bl + 10), seq_num(Th, Bl + 30))
+        ]
+
+        self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        """
+            case f: Seql > Tl
+                - pre-crypto check: algorithm predicts that the packet does not wrap the window
+                    -> Seqh = Th
+                - integrity check: should pass
+                - post-crypto check: should pass
+                    -> AR window shift (the window stays Case A)
+                    -> Seql is marked in the AR window
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th, Tl + 50), seq_num(Th, Tl + 60))
+        ]
+
+        self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        """
+            case g: Seql < Bl
+                - pre-crypto check: algorithm predicts that the packet wrap the window
+                    -> Seqh = Th + 1
+                - integrity check: should pass
+                - post-crypto check: should pass
+                    -> AR window shift (may set the window in Case B)
+                    -> Seql is marked in the AR window
+        """
+        p.scapy_tra_sa.seq_num = seq_num(Th + 1, Tl)
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            # set the window in Case B (the minimum window size is 64
+            # so we are sure to overlap)
+            for seq in range(seq_num(Th + 1, 10), seq_num(Th + 1, 20))
+        ]
+
+        self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        # reset the VPP's RX AR window to Case A
+        Th = 1
+        Tl = 2 * anti_replay_window_size + 40
+        Bl = Tl - anti_replay_window_size + 1
+
+        self.vapi.cli(f"test ipsec sa {p.scapy_tra_sa_id} seq {seq_num(Th, Tl):#x}")
+
+        p.scapy_tra_sa.seq_num = seq_num(Th + 1, Tl)
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            # the AR will stay in Case A
+            for seq in range(
+                seq_num(Th + 1, anti_replay_window_size + 10),
+                seq_num(Th + 1, anti_replay_window_size + 20),
+            )
+        ]
+
+        self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        """
+            case h: Bl <= Seql <= Tl
+                - pre-crypto check: algorithm predicts that the packet is in the window
+                    -> Seqh = Th
+                    -> check for a replayed packet with Seql
+                - integrity check: the wrap is not detected, should fail
+                - post-crypto check: ...
+        """
+        Th += 1
+        Tl = anti_replay_window_size + 20
+        Bl = Tl - anti_replay_window_size + 1
+
+        p.scapy_tra_sa.seq_num = seq_num(Th + 1, Tl)
+
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th + 1, Tl - 20), seq_num(Th + 1, Tl - 5))
+        ]
+
+        self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2)
+
+        # some packets are rejected by the pre-crypto check
+        replay_count += 5
+        self.assertEqual(self.get_replay_counts(p), replay_count)
+
+        # out-of-window packets fail integrity check
+        hash_failed_count += len(pkts) - 5
+        self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count)
+
+        """
+            case i: Seql > Tl
+                - pre-crypto check: algorithm predicts that the packet does not wrap the window
+                    -> Seqh = Th
+                - integrity check: the wrap is not detected, shoud fail
+                - post-crypto check: ...
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th + 1, Tl + 5), seq_num(Th + 1, Tl + 15))
+        ]
+
+        # out-of-window packets fail integrity check
+        self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2)
+        hash_failed_count += len(pkts)
+        self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count)
+
+        """
+            RFC 4303 Appendix A2. Case B
+
+                        Th - 1               Th                Th + 1
+            ----|-a-+-----b----+--c--|-d-+----e-----+--f--|-g-+--h---
+            =========          ===========          ===========
+                    Tl-        Bl       Tl          Bl+       Tl+
+
+            Case B implies Tl < W - 1
+        """
+
+        # reset the VPP's RX AR window to Case B
+        Th = 2
+        Tl = 30  # minimum window size of 64, we are sure to overlap
+        Bl = (Tl - anti_replay_window_size + 1) % (1 << 32)
+
+        self.vapi.cli(f"test ipsec sa {p.scapy_tra_sa_id} seq {seq_num(Th, Tl):#x}")
+        p.scapy_tra_sa.seq_num = seq_num(Th, Tl)
+
+        """
+            case a: Seql <= Tl < Bl
+                - pre-crypto check: algorithm predicts that the packet is in the window
+                    -> Seqh = Th
+                    -> check for replayed packet
+                - integrity check: should fail
+                - post-crypto check: ...
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th, 5), seq_num(Th, 10))
+        ]
+
+        self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        p.scapy_tra_sa.seq_num = seq_num(Th - 1, Tl)
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th - 1, 0), seq_num(Th - 1, 15))
+        ]
+
+        self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2)
+
+        # some packets are rejected by the pre-crypto check
+        replay_count += 5
+        self.assertEqual(self.get_replay_counts(p), replay_count)
+
+        # out-of-window packets fail integrity check
+        hash_failed_count += len(pkts) - 5
+        self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count)
+
+        """
+            case b: Tl < Seql < Bl
+                - pre-crypto check: algorithm predicts that the packet will shift the window
+                    -> Seqh = Th
+                - integrity check: should fail
+                - post-crypto check: ...
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th - 1, Tl + 10), seq_num(Th - 1, Tl + 20))
+        ]
+
+        self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2)
+
+        # out-of-window packets fail integrity check
+        hash_failed_count += len(pkts)
+        self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count)
+
+        """
+            case c: Tl < Bl <= Seql
+                - pre-crypto check: algorithm predicts that the packet is in the window
+                    -> Seqh = Th - 1
+                    -> check for a replayed packet with Seql
+                - integrity check: should pass
+                - post-crypto check: should pass
+                    -> Seql is marked in the AR window
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th - 1, Bl + 10), seq_num(Th - 1, Bl + 20))
+        ]
+
+        self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        """
+            case d: Seql <= Tl < Bl
+                - pre-crypto check: algorithm predicts that the packet is the window
+                    -> Seqh = Th
+                    -> check for replayed packet
+                - integrity check: should pass
+                - post-crypto check: should pass
+                    -> Seql is marked in the AR window
+        """
+        p.scapy_tra_sa.seq_num = seq_num(Th, Tl)
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th, 15), seq_num(Th, 25))
+        ]
+
+        self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        """
+            case e: Tl < Seql < Bl
+                - pre-crypto check: algorithm predicts that the packet is in the window
+                    -> Seqh = Th
+                    -> check for a replayed packet with Seql
+                - integrity check: should pass
+                - post-crypto check: should pass
+                    -> AR window shift (may set the window in Case A)
+                    -> Seql is marked in the AR window
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th, Tl + 5), seq_num(Th, Tl + 15))
+        ]
+
+        # the window stays in Case B
+        self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(
+                seq_num(Th, Tl + anti_replay_window_size + 5),
+                seq_num(Th, Tl + anti_replay_window_size + 15),
+            )
+        ]
+
+        # the window moves to Case A
+        self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        # reset the VPP's RX AR window to Case B
+        Th = 2
+        Tl = 30  # minimum window size of 64, we are sure to overlap
+        Bl = (Tl - anti_replay_window_size + 1) % (1 << 32)
+
+        self.vapi.cli(f"test ipsec sa {p.scapy_tra_sa_id} seq {seq_num(Th, Tl):#x}")
+        p.scapy_tra_sa.seq_num = seq_num(Th, Tl)
+
+        """
+            case f: Tl < Bl <= Seql
+                - pre-crypto check: algorithm predicts that the packet is in the previous window
+                    -> Seqh = Th - 1
+                    -> check for a replayed packet with Seql
+                - integrity check: should fail
+                - post-crypto check: ...
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th, Bl + 10), seq_num(Th, Bl + 20))
+        ]
+
+        self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2)
+
+        # out-of-window packets fail integrity check
+        hash_failed_count += len(pkts)
+        self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count)
+
+        """
+            case g: Seql <= Tl < Bl
+                - pre-crypto check: algorithm predicts that the packet is the window
+                    -> Seqh = Th
+                    -> check for replayed packet
+                - integrity check: should fail
+                - post-crypto check: ...
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th, 10), seq_num(Th, 15))
+        ]
+
+        self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        p.scapy_tra_sa.seq_num = seq_num(Th + 1, Tl)
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th + 1, 0), seq_num(Th + 1, 15))
+        ]
+
+        self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2)
+
+        # some packets are rejected by the pre-crypto check
+        replay_count += 5
+        self.assertEqual(self.get_replay_counts(p), replay_count)
+
+        # out-of-window packets fail integrity check
+        hash_failed_count += len(pkts) - 5
+        self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count)
+
+        """
+            case h: Tl < Seql < Bl
+                - pre-crypto check: algorithm predicts that the packet will shift the window
+                    -> Seqh = Th
+                - integrity check: should fail
+                - post-crypto check: ...
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Th + 1, Tl + 10), seq_num(Th + 1, Tl + 20))
+        ]
+
+        self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2)
+
+        # out-of-window packets fail integrity check
+        hash_failed_count += len(pkts)
+        self.assertEqual(self.get_hash_failed_counts(p), hash_failed_count)
+
+    def _verify_tra_anti_replay_algorithm_no_esn(self):
+        def seq_num(seql):
+            return seql & 0xFFFF_FFFF
+
+        p = self.params[socket.AF_INET]
+        anti_replay_window_size = p.anti_replay_window_size
+
+        seq_cycle_node_name = "/err/%s/seq_cycled" % self.tra4_encrypt_node_name
+        replay_count = self.get_replay_counts(p)
+        hash_failed_count = self.get_hash_failed_counts(p)
+        seq_cycle_count = self.statistics.get_err_counter(seq_cycle_node_name)
+
+        if ESP == self.encryption_type:
+            undersize_node_name = "/err/%s/runt" % self.tra4_decrypt_node_name[0]
+            undersize_count = self.statistics.get_err_counter(undersize_node_name)
+
+        # reset the TX SA to avoid conflict with left configuration
+        self.vapi.cli(f"test ipsec sa {p.vpp_tra_sa_id} seq 0x0")
+
+        """
+        RFC 4303 Appendix A2. Case A
+
+        a-c: possible seq num received
+        +: Bl, Tl
+
+        |--a--+---b---+-c--|
+              =========
+              Bl      Tl
+
+        No ESN implies Th = 0
+        Case A implies Tl >= W - 1
+        """
+
+        Tl = anti_replay_window_size + 40
+        Bl = Tl - anti_replay_window_size + 1
+
+        # move VPP's RX AR window to Case A
+        self.vapi.cli(f"test ipsec sa {p.scapy_tra_sa_id} seq {seq_num(Tl):#x}")
+        p.scapy_tra_sa.seq_num = seq_num(Tl)
+
+        """
+        case a: Seql < Bl
+            - pre-crypto check: algorithm predicts that the packet is out of window
+                -> packet should be dropped
+            - integrity check: ...
+            - post-crypto check: ...
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Bl - 20), seq_num(Bl - 5))
+        ]
+
+        # out-of-window packets
+        self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2)
+        replay_count += len(pkts)
+        self.assertEqual(self.get_replay_counts(p), replay_count)
+
+        """
+            case b: Bl <= Seql <= Tl
+                - pre-crypto check: algorithm predicts that the packet is in the window
+                    -> check for a replayed packet with Seql
+                - integrity check: should pass
+                - post-crypto check:
+                    -> check for a replayed packet with Seql
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Tl - 50), seq_num(Tl - 30))
+        ]
+        self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Tl - 35), seq_num(Tl - 30))
+        ]
+
+        self.send_and_assert_no_replies(self.tra_if, pkts, timeout=0.2)
+
+        # replayed packets
+        replay_count += 5
+        self.assertEqual(self.get_replay_counts(p), replay_count)
+
+        """
+            case c: Seql > Tl
+                - pre-crypto check: algorithm predicts that the packet will shift the window
+                - integrity check: should pass
+                - post-crypto check: should pass
+                    -> AR window is shifted
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(Tl + 5), seq_num(Tl + 20))
+        ]
+
+        self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        """
+            RFC 4303 Appendix A2. Case B
+
+            |-a-----+------b-----|
+            =========
+                    Tl
+
+            Case B implies Tl < W - 1
+        """
+
+        # reset the VPP's RX AR window to Case B
+        Tl = 30  # minimum window size of 64, we are sure to overlap
+        Bl = seq_num(Tl - anti_replay_window_size + 1)
+
+        self.vapi.cli(f"test ipsec sa {p.scapy_tra_sa_id} seq {seq_num(Tl):#x}")
+
+        """
+        case a: Seql <= Tl < Bl
+            - pre-crypto check: algorithm predicts that the packet is in the window
+                -> check for replayed packet
+            - integrity check: should fail
+            - post-crypto check: ...
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(5), seq_num(10))
+        ]
+
+        self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+        """
+            case b: Tl < Seql < Bl
+                - pre-crypto check: algorithm predicts that the packet will shift the window
+                - integrity check: should pass
+                - post-crypto check: should pass
+                    -> AR window is shifted
+        """
+        pkts = [
+            (
+                Ether(src=self.tra_if.remote_mac, dst=self.tra_if.local_mac)
+                / p.scapy_tra_sa.encrypt(
+                    IP(src=self.tra_if.remote_ip4, dst=self.tra_if.local_ip4) / ICMP(),
+                    seq_num=seq,
+                )
+            )
+            for seq in range(seq_num(-50), seq_num(-20))
+        ]
+
+        self.send_and_expect(self.tra_if, pkts, self.tra_if)
+
+    def verify_tra_anti_replay_algorithm(self):
+        if self.params[socket.AF_INET].vpp_tra_sa.esn_en:
+            self._verify_tra_anti_replay_algorithm_esn()
+        else:
+            self._verify_tra_anti_replay_algorithm_no_esn()
+
 
 class IpsecTra4Tests(IpsecTra4):
     """UT test methods for Transport v4"""
@@ -1076,6 +1855,10 @@ class IpsecTra4Tests(IpsecTra4):
         """ipsec v4 transport anti-replay test"""
         self.verify_tra_anti_replay()
 
+    def test_tra_anti_replay_algorithm(self):
+        """ipsec v4 transport anti-replay algorithm test"""
+        self.verify_tra_anti_replay_algorithm()
+
     def test_tra_lost(self):
         """ipsec v4 transport lost packet test"""
         self.verify_tra_lost()
index 521762b..6e246f6 100644 (file)
@@ -121,7 +121,7 @@ class IpsecApiTestCase(VppTestCase):
 
     def __check_sa_binding(self, sa_id, thread_index):
         found_sa = False
-        sa_dumps = self.vapi.ipsec_sa_v4_dump()
+        sa_dumps = self.vapi.ipsec_sa_v5_dump()
         for dump in sa_dumps:
             if dump.entry.sad_id == sa_id:
                 self.assertEqual(dump.thread_index, thread_index)
index 927863c..fdd7eb8 100644 (file)
@@ -62,10 +62,11 @@ class ConfigIpsecESP(TemplateIpsec):
     def tearDown(self):
         super(ConfigIpsecESP, self).tearDown()
 
-    def config_anti_replay(self, params):
+    def config_anti_replay(self, params, anti_replay_window_size=64):
         saf = VppEnum.vl_api_ipsec_sad_flags_t
         for p in params:
             p.flags |= saf.IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY
+            p.anti_replay_window_size = anti_replay_window_size
 
     def config_network(self, params):
         self.net_objs = []
@@ -134,6 +135,7 @@ class ConfigIpsecESP(TemplateIpsec):
         flags = params.flags
         tun_flags = params.tun_flags
         salt = params.salt
+        anti_replay_window_size = params.anti_replay_window_size
         objs = []
 
         params.tun_sa_in = VppIpsecSA(
@@ -152,6 +154,7 @@ class ConfigIpsecESP(TemplateIpsec):
             flags=flags,
             salt=salt,
             hop_limit=params.outer_hop_limit,
+            anti_replay_window_size=anti_replay_window_size,
         )
         params.tun_sa_out = VppIpsecSA(
             self,
@@ -169,6 +172,7 @@ class ConfigIpsecESP(TemplateIpsec):
             flags=flags,
             salt=salt,
             hop_limit=params.outer_hop_limit,
+            anti_replay_window_size=anti_replay_window_size,
         )
         objs.append(params.tun_sa_in)
         objs.append(params.tun_sa_out)
@@ -274,6 +278,7 @@ class ConfigIpsecESP(TemplateIpsec):
         e = VppEnum.vl_api_ipsec_spd_action_t
         flags = params.flags
         salt = params.salt
+        anti_replay_window_size = params.anti_replay_window_size
         objs = []
 
         params.tra_sa_in = VppIpsecSA(
@@ -287,6 +292,7 @@ class ConfigIpsecESP(TemplateIpsec):
             self.vpp_esp_protocol,
             flags=flags,
             salt=salt,
+            anti_replay_window_size=anti_replay_window_size,
         )
         params.tra_sa_out = VppIpsecSA(
             self,
@@ -299,6 +305,7 @@ class ConfigIpsecESP(TemplateIpsec):
             self.vpp_esp_protocol,
             flags=flags,
             salt=salt,
+            anti_replay_window_size=anti_replay_window_size,
         )
         objs.append(params.tra_sa_in)
         objs.append(params.tra_sa_out)
@@ -1184,9 +1191,16 @@ class RunTestIpsecEspAll(ConfigIpsecESP, IpsecTra4, IpsecTra6, IpsecTun4, IpsecT
         #
         saf = VppEnum.vl_api_ipsec_sad_flags_t
         if flag & saf.IPSEC_API_SAD_FLAG_USE_ANTI_REPLAY:
-            self.unconfig_network()
-            self.config_network(self.params.values())
-            self.verify_tra_anti_replay()
+            for anti_replay_window_size in (
+                64,
+                131072,
+            ):
+                self.unconfig_network()
+                self.config_anti_replay(self.params.values(), anti_replay_window_size)
+                self.config_network(self.params.values())
+                self.verify_tra_anti_replay()
+                self.verify_tra_anti_replay_algorithm()
+            self.config_anti_replay(self.params.values())
 
         self.unconfig_network()
         self.config_network(self.params.values())
index 7a5a95a..e354cfc 100644 (file)
@@ -218,6 +218,7 @@ class VppIpsecSA(VppObject):
         udp_src=None,
         udp_dst=None,
         hop_limit=None,
+        anti_replay_window_size=0,
     ):
         e = VppEnum.vl_api_ipsec_sad_flags_t
         self.test = test
@@ -229,6 +230,7 @@ class VppIpsecSA(VppObject):
         self.crypto_key = crypto_key
         self.proto = proto
         self.salt = salt
+        self.anti_replay_window_size = anti_replay_window_size
 
         self.table_id = 0
         self.tun_src = tun_src
@@ -284,13 +286,14 @@ class VppIpsecSA(VppObject):
             "tunnel": self.tunnel_encode(),
             "flags": self.flags,
             "salt": self.salt,
+            "anti_replay_window_size": self.anti_replay_window_size,
         }
         # don't explicitly send the defaults, let papi fill them in
         if self.udp_src:
             entry["udp_src_port"] = self.udp_src
         if self.udp_dst:
             entry["udp_dst_port"] = self.udp_dst
-        r = self.test.vapi.ipsec_sad_entry_add(entry=entry)
+        r = self.test.vapi.ipsec_sad_entry_add_v2(entry=entry)
         self.stat_index = r.stat_index
         self.test.registry.register(self, self.test.logger)
         return self
@@ -324,7 +327,7 @@ class VppIpsecSA(VppObject):
     def query_vpp_config(self):
         e = VppEnum.vl_api_ipsec_sad_flags_t
 
-        bs = self.test.vapi.ipsec_sa_v3_dump()
+        bs = self.test.vapi.ipsec_sa_v5_dump()
         for b in bs:
             if b.entry.sad_id == self.id:
                 # if udp encap is configured then the ports should match