aboutsummaryrefslogtreecommitdiffstats
path: root/src/vnet/ipsec/ipsec_sa.h
diff options
context:
space:
mode:
authorNeale Ranns <neale@graphiant.com>2021-06-28 13:31:28 +0000
committerBeno�t Ganne <bganne@cisco.com>2021-06-29 17:12:28 +0000
commit5b8911020ee5512d76c8daccaa199878ed7cbc01 (patch)
tree0c0960f5ffc12f1fe3d1a354a0736bd9ac374a07 /src/vnet/ipsec/ipsec_sa.h
parent3871bd3a07dfc775c29ea333d10c05ced2fea80e (diff)
ipsec: Fix setting the hi-sequence number for decrypt
Type: fix two problems; 1 - just because anti-reply is not enabled doesn't mean the high sequence number should not be used. - fix, there needs to be some means to detect a wrapped packet, so we use a window size of 2^30. 2 - The SA object was used as a scratch pad for the high-sequence number used during decryption. That means that once the batch has been processed the high-sequence number used is lost. This means it is not possible to distinguish this case: if (seq < IPSEC_SA_ANTI_REPLAY_WINDOW_LOWER_BOUND (tl)) { ... if (post_decrypt) { 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. * that means this packet did not cause a wrap. * this packet is thus out of window and should be dropped */ return 1; else /* The packet decrypted with a different high sequence number * to the SA, that means it is the wrap packet and should be * accepted */ return 0; } - fix: don't use the SA as a scratch pad, use the 'packet_data' - the same place that is used as the scratch pad for the low sequence number. other consequences: - An SA doesn't have seq and last_seq, it has only seq; the sequence numnber of the last packet tx'd or rx'd. - there's 64bits of space available on the SA's first cache line. move the AES CTR mode IV there. - test the ESN/AR combinations to catch the bugs this fixes. This doubles the amount of tests, but without AR on they only run for 2 seconds. In the AR tests, the time taken to wait for packets that won't arrive is dropped from 1 to 0.2 seconds thus reducing the runtime of these tests from 10-15 to about 5 sceonds. Signed-off-by: Neale Ranns <neale@graphiant.com> Change-Id: Iaac78905289a272dc01930d70decd8109cf5e7a5
Diffstat (limited to 'src/vnet/ipsec/ipsec_sa.h')
-rw-r--r--src/vnet/ipsec/ipsec_sa.h178
1 files changed, 132 insertions, 46 deletions
diff --git a/src/vnet/ipsec/ipsec_sa.h b/src/vnet/ipsec/ipsec_sa.h
index 7827ef18084..14461ad2cdd 100644
--- a/src/vnet/ipsec/ipsec_sa.h
+++ b/src/vnet/ipsec/ipsec_sa.h
@@ -131,9 +131,8 @@ typedef struct
u32 spi;
u32 seq;
u32 seq_hi;
- u32 last_seq;
- u32 last_seq_hi;
u64 replay_window;
+ u64 ctr_iv_counter;
dpo_id_t dpo;
vnet_crypto_key_index_t crypto_key_index;
@@ -162,7 +161,6 @@ typedef struct
CLIB_CACHE_LINE_ALIGN_MARK (cacheline1);
- u64 ctr_iv_counter;
union
{
ip4_header_t ip4_hdr;
@@ -312,45 +310,104 @@ extern uword unformat_ipsec_key (unformat_input_t * input, va_list * args);
*/
#define IPSEC_SA_ANTI_REPLAY_WINDOW_LOWER_BOUND(_tl) (_tl - IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE + 1)
+always_inline int
+ipsec_sa_anti_replay_check (const ipsec_sa_t *sa, u32 seq)
+{
+ if (ipsec_sa_is_set_USE_ANTI_REPLAY (sa) &&
+ sa->replay_window & (1ULL << (sa->seq - seq)))
+ return 1;
+ else
+ return 0;
+}
+
/*
* Anti replay check.
* inputs need to be in host byte order.
+ *
+ * The function runs in two contexts. pre and post decrypt.
+ * Pre-decrypt it:
+ * 1 - determines if a packet is a replay - a simple check in the window
+ * 2 - returns the hi-seq number that should be used to decrypt.
+ * post-decrypt:
+ * Checks whether the packet is a replay or falls out of window
+ *
+ * This funcion should be called even without anti-replay enabled to ensure
+ * the high sequence number is set.
*/
always_inline int
-ipsec_sa_anti_replay_check (ipsec_sa_t * sa, u32 seq)
+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 diff, tl, th;
-
- if ((sa->flags & IPSEC_SA_FLAG_USE_ANTI_REPLAY) == 0)
- return 0;
+ ASSERT ((post_decrypt == false) == (hi_seq_req != 0));
if (!ipsec_sa_is_set_USE_ESN (sa))
{
- if (PREDICT_TRUE (seq > sa->last_seq))
+ if (hi_seq_req)
+ /* no ESN, therefore the hi-seq is always 0 */
+ *hi_seq_req = 0;
+
+ if (!ipsec_sa_is_set_USE_ANTI_REPLAY (sa))
return 0;
- diff = sa->last_seq - 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;
+ return ((sa->replay_window & (1ULL << diff)) ? 1 : 0);
else
return 1;
return 0;
}
- tl = sa->last_seq;
- th = sa->last_seq_hi;
- diff = tl - seq;
-
- if (PREDICT_TRUE (tl >= (IPSEC_SA_ANTI_REPLAY_WINDOW_MAX_INDEX)))
+ if (!ipsec_sa_is_set_USE_ANTI_REPLAY (sa))
+ {
+ /* there's no AR configured for this SA, but in order
+ * to know whether a packet has wrapped the hi ESN we need
+ * to know whether it is out of window. if we use the default
+ * lower bound then we are effectively forcing AR because
+ * out of window packets will get the increased hi seq number
+ * and will thus fail to decrypt. IOW we need a window to know
+ * if the SN has wrapped, but we don't want a window to check for
+ * anti replay. to resolve the contradiction we use a huge window.
+ * if the packet is not within 2^30 of the current SN, we'll consider
+ * it a wrap.
+ */
+ if (hi_seq_req)
+ {
+ if (seq >= sa->seq)
+ /* The packet's sequence number is larger that the SA's.
+ * that can't be a warp - unless we lost more than
+ * 2^32 packets ... how could we know? */
+ *hi_seq_req = sa->seq_hi;
+ else
+ {
+ /* The packet's SN is less than the SAs, so either the SN has
+ * wrapped or the SN is just old. */
+ if (sa->seq - seq > (1 << 30))
+ /* It's really really really old => it wrapped */
+ *hi_seq_req = sa->seq_hi + 1;
+ else
+ *hi_seq_req = sa->seq_hi;
+ }
+ }
+ /*
+ * else
+ * this is post-decrpyt and since it decrypted we accept it
+ */
+ return 0;
+ }
+ if (PREDICT_TRUE (sa->seq >= (IPSEC_SA_ANTI_REPLAY_WINDOW_MAX_INDEX)))
{
/*
* the last sequence number VPP recieved is more than one
* window size greater than zero.
* Case A from RFC4303 Appendix A.
*/
- if (seq < IPSEC_SA_ANTI_REPLAY_WINDOW_LOWER_BOUND (tl))
+ if (seq < IPSEC_SA_ANTI_REPLAY_WINDOW_LOWER_BOUND (sa->seq))
{
/*
* the received sequence number is lower than the lower bound
@@ -358,8 +415,28 @@ ipsec_sa_anti_replay_check (ipsec_sa_t * sa, u32 seq)
* the high sequence number has wrapped. if it decrypts corrently
* then it's the latter.
*/
- sa->seq_hi = th + 1;
- return 0;
+ if (post_decrypt)
+ {
+ 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.
+ * that means this packet did not cause a wrap.
+ * this packet is thus out of window and should be dropped */
+ return 1;
+ else
+ /* The packet decrypted with a different high sequence number
+ * to the SA, that means it is the wrap packet and should be
+ * accepted */
+ return 0;
+ }
+ else
+ {
+ /* pre-decrypt it might be the might that casues a wrap, we
+ * need to decrpyt to find out */
+ if (hi_seq_req)
+ *hi_seq_req = sa->seq_hi + 1;
+ return 0;
+ }
}
else
{
@@ -367,13 +444,14 @@ ipsec_sa_anti_replay_check (ipsec_sa_t * sa, u32 seq)
* the recieved sequence number greater than the low
* end of the window.
*/
- sa->seq_hi = th;
- if (seq <= tl)
+ if (hi_seq_req)
+ *hi_seq_req = sa->seq_hi;
+ if (seq <= sa->seq)
/*
* The recieved seq number is within bounds of the window
* check if it's a duplicate
*/
- return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
+ return (ipsec_sa_anti_replay_check (sa, seq));
else
/*
* The received sequence number is greater than the window
@@ -393,19 +471,20 @@ ipsec_sa_anti_replay_check (ipsec_sa_t * sa, u32 seq)
* RHS will be a larger number.
* Case B from RFC4303 Appendix A.
*/
- if (seq < IPSEC_SA_ANTI_REPLAY_WINDOW_LOWER_BOUND (tl))
+ if (seq < IPSEC_SA_ANTI_REPLAY_WINDOW_LOWER_BOUND (sa->seq))
{
/*
* the sequence number is less than the lower bound.
*/
- if (seq <= tl)
+ if (seq <= sa->seq)
{
/*
* the packet is within the window upper bound.
* check for duplicates.
*/
- sa->seq_hi = th;
- return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
+ if (hi_seq_req)
+ *hi_seq_req = sa->seq_hi;
+ return (ipsec_sa_anti_replay_check (sa, seq));
}
else
{
@@ -418,7 +497,8 @@ ipsec_sa_anti_replay_check (ipsec_sa_t * sa, u32 seq)
* wrapped the high sequence again. If it were the latter then
* we've lost close to 2^32 packets.
*/
- sa->seq_hi = th;
+ if (hi_seq_req)
+ *hi_seq_req = sa->seq_hi;
return 0;
}
}
@@ -431,73 +511,79 @@ ipsec_sa_anti_replay_check (ipsec_sa_t * sa, u32 seq)
* However, since TL is the other side of 0 to the received
* packet, the SA has moved on to a higher sequence number.
*/
- sa->seq_hi = th - 1;
- return (sa->replay_window & (1ULL << diff)) ? 1 : 0;
+ if (hi_seq_req)
+ *hi_seq_req = sa->seq_hi - 1;
+ return (ipsec_sa_anti_replay_check (sa, seq));
}
}
+ /* unhandled case */
+ ASSERT (0);
return 0;
}
/*
* Anti replay window advance
* inputs need to be in host byte order.
+ * This function both advances the anti-replay window and the sequence number
+ * We always need to move on the SN but the window updates are only needed
+ * if AR is on.
+ * However, updating the window is trivial, so we do it anyway to save
+ * the branch cost.
*/
always_inline void
-ipsec_sa_anti_replay_advance (ipsec_sa_t * sa, u32 seq)
+ipsec_sa_anti_replay_advance (ipsec_sa_t *sa, u32 seq, u32 hi_seq)
{
u32 pos;
- if (PREDICT_TRUE (sa->flags & IPSEC_SA_FLAG_USE_ANTI_REPLAY) == 0)
- return;
- if (PREDICT_TRUE (sa->flags & IPSEC_SA_FLAG_USE_ESN))
+ if (ipsec_sa_is_set_USE_ESN (sa))
{
- int wrap = sa->seq_hi - sa->last_seq_hi;
+ int wrap = hi_seq - sa->seq_hi;
- if (wrap == 0 && seq > sa->last_seq)
+ if (wrap == 0 && seq > sa->seq)
{
- pos = seq - sa->last_seq;
+ pos = seq - sa->seq;
if (pos < IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE)
sa->replay_window = ((sa->replay_window) << pos) | 1;
else
sa->replay_window = 1;
- sa->last_seq = seq;
+ sa->seq = seq;
}
else if (wrap > 0)
{
- pos = ~seq + sa->last_seq + 1;
+ pos = ~seq + sa->seq + 1;
if (pos < IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE)
sa->replay_window = ((sa->replay_window) << pos) | 1;
else
sa->replay_window = 1;
- sa->last_seq = seq;
- sa->last_seq_hi = sa->seq_hi;
+ sa->seq = seq;
+ sa->seq_hi = hi_seq;
}
else if (wrap < 0)
{
- pos = ~seq + sa->last_seq + 1;
+ pos = ~seq + sa->seq + 1;
sa->replay_window |= (1ULL << pos);
}
else
{
- pos = sa->last_seq - seq;
+ pos = sa->seq - seq;
sa->replay_window |= (1ULL << pos);
}
}
else
{
- if (seq > sa->last_seq)
+ if (seq > sa->seq)
{
- pos = seq - sa->last_seq;
+ pos = seq - sa->seq;
if (pos < IPSEC_SA_ANTI_REPLAY_WINDOW_SIZE)
sa->replay_window = ((sa->replay_window) << pos) | 1;
else
sa->replay_window = 1;
- sa->last_seq = seq;
+ sa->seq = seq;
}
else
{
- pos = sa->last_seq - seq;
+ pos = sa->seq - seq;
sa->replay_window |= (1ULL << pos);
}
}