aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMaxime Peim <mpeim@cisco.com>2022-12-22 11:26:57 +0000
committerBeno�t Ganne <bganne@cisco.com>2023-10-30 15:23:13 +0000
commit0e2f188f7c9872d7c946c14d785c6dc7c7c68847 (patch)
tree1adc39db5e2e0e243811c8ce001d0bd056c0402e /src
parent21922cec7339f48989f230248de36a98816c4b1b (diff)
ipsec: huge anti-replay window support
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
Diffstat (limited to 'src')
-rw-r--r--src/plugins/ikev2/ikev2.c4
-rw-r--r--src/plugins/unittest/ipsec_test.c19
-rw-r--r--src/vnet/ipsec/ah_decrypt.c46
-rw-r--r--src/vnet/ipsec/esp_decrypt.c59
-rw-r--r--src/vnet/ipsec/ipsec.api33
-rw-r--r--src/vnet/ipsec/ipsec_api.c196
-rw-r--r--src/vnet/ipsec/ipsec_cli.c12
-rw-r--r--src/vnet/ipsec/ipsec_format.c14
-rw-r--r--src/vnet/ipsec/ipsec_sa.c20
-rw-r--r--src/vnet/ipsec/ipsec_sa.h379
-rw-r--r--src/vnet/ipsec/ipsec_test.c45
-rw-r--r--src/vnet/ipsec/ipsec_types.api44
-rw-r--r--src/vppinfra/bitmap.h2
13 files changed, 695 insertions, 178 deletions
diff --git a/src/plugins/ikev2/ikev2.c b/src/plugins/ikev2/ikev2.c
index 3e808736078..ad36068c34d 100644
--- a/src/plugins/ikev2/ikev2.c
+++ b/src/plugins/ikev2/ikev2.c
@@ -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;
diff --git a/src/plugins/unittest/ipsec_test.c b/src/plugins/unittest/ipsec_test.c
index 55fd031b9b9..bb7f2a8d9e2 100644
--- a/src/plugins/unittest/ipsec_test.c
+++ b/src/plugins/unittest/ipsec_test.c
@@ -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,
diff --git a/src/vnet/ipsec/ah_decrypt.c b/src/vnet/ipsec/ah_decrypt.c
index 5f98693204a..62a6b706362 100644
--- a/src/vnet/ipsec/ah_decrypt.c
+++ b/src/vnet/ipsec/ah_decrypt.c
@@ -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);
diff --git a/src/vnet/ipsec/esp_decrypt.c b/src/vnet/ipsec/esp_decrypt.c
index 74410a8add1..7b74e6e2c67 100644
--- a/src/vnet/ipsec/esp_decrypt.c
+++ b/src/vnet/ipsec/esp_decrypt.c
@@ -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,
diff --git a/src/vnet/ipsec/ipsec.api b/src/vnet/ipsec/ipsec.api
index ad1d21618e6..68efe8f50f7 100644
--- a/src/vnet/ipsec/ipsec.api
+++ b/src/vnet/ipsec/ipsec.api
@@ -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;
};
diff --git a/src/vnet/ipsec/ipsec_api.c b/src/vnet/ipsec/ipsec_api.c
index 7fc68f60369..6e7308ddf75 100644
--- a/src/vnet/ipsec/ipsec_api.c
+++ b/src/vnet/ipsec/ipsec_api.c
@@ -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)
{
@@ -568,6 +618,19 @@ vl_api_ipsec_sad_entry_add_t_handler (vl_api_ipsec_sad_entry_add_t *mp)
}
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)
{
vl_api_ipsec_sad_entry_update_reply_t *rmp;
@@ -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;
diff --git a/src/vnet/ipsec/ipsec_cli.c b/src/vnet/ipsec/ipsec_cli.c
index 6cb9aba4d8a..5aef630a33f 100644
--- a/src/vnet/ipsec/ipsec_cli.c
+++ b/src/vnet/ipsec/ipsec_cli.c
@@ -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
{
diff --git a/src/vnet/ipsec/ipsec_format.c b/src/vnet/ipsec/ipsec_format.c
index d1511acdc26..12381ceaa13 100644
--- a/src/vnet/ipsec/ipsec_format.c
+++ b/src/vnet/ipsec/ipsec_format.c
@@ -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
diff --git a/src/vnet/ipsec/ipsec_sa.c b/src/vnet/ipsec/ipsec_sa.c
index 98160cde389..1656b9aa0cc 100644
--- a/src/vnet/ipsec/ipsec_sa.c
+++ b/src/vnet/ipsec/ipsec_sa.c
@@ -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);
}
diff --git a/src/vnet/ipsec/ipsec_sa.h b/src/vnet/ipsec/ipsec_sa.h
index cd90ef5c441..47058511036 100644
--- a/src/vnet/ipsec/ipsec_sa.h
+++ b/src/vnet/ipsec/ipsec_sa.h
@@ -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));
}
}
diff --git a/src/vnet/ipsec/ipsec_test.c b/src/vnet/ipsec/ipsec_test.c
index 9b42ae71a80..86d09f18a5c 100644
--- a/src/vnet/ipsec/ipsec_test.c
+++ b/src/vnet/ipsec/ipsec_test.c
@@ -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)
{
@@ -342,6 +348,18 @@ api_ipsec_sa_v3_dump (vat_main_t *vat)
}
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)
{
return -1;
@@ -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)
{
@@ -446,17 +480,6 @@ api_ipsec_sa_dump (vat_main_t *vam)
}
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)
{
vat_main_t *vam = &vat_main;
diff --git a/src/vnet/ipsec/ipsec_types.api b/src/vnet/ipsec/ipsec_types.api
index 9adcc6aa8eb..37c1141ab46 100644
--- a/src/vnet/ipsec/ipsec_types.api
+++ b/src/vnet/ipsec/ipsec_types.api
@@ -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:
diff --git a/src/vppinfra/bitmap.h b/src/vppinfra/bitmap.h
index abb1405d3a2..99d65954d09 100644
--- a/src/vppinfra/bitmap.h
+++ b/src/vppinfra/bitmap.h
@@ -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