Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F108989504
D22367.id64314.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D22367.id64314.diff
View Options
Index: sys/netipsec/ipsec.h
===================================================================
--- sys/netipsec/ipsec.h
+++ sys/netipsec/ipsec.h
@@ -332,7 +332,7 @@
int udp_ipsec_input(struct mbuf *, int, int);
int udp_ipsec_pcbctl(struct inpcb *, struct sockopt *);
-int ipsec_chkreplay(uint32_t, struct secasvar *);
+int ipsec_chkreplay(uint32_t, uint32_t *, struct secasvar *);
int ipsec_updatereplay(uint32_t, struct secasvar *);
int ipsec_updateid(struct secasvar *, crypto_session_t *, crypto_session_t *);
int ipsec_initialized(void);
Index: sys/netipsec/ipsec.c
===================================================================
--- sys/netipsec/ipsec.c
+++ sys/netipsec/ipsec.c
@@ -1176,6 +1176,66 @@
return (sz);
}
+
+#define IPSEC_BITMAP_INDEX_MASK(w) (w - 1)
+#define IPSEC_REDUNDANT_BIT_SHIFTS 5
+#define IPSEC_REDUNDANT_BITS (1 << IPSEC_REDUNDANT_BIT_SHIFTS)
+#define IPSEC_BITMAP_LOC_MASK (IPSEC_REDUNDANT_BITS - 1)
+
+/*
+ * Functions below are responsible for checking and updating bitmap.
+ * It was meant to seperate ipsec_chkreplay() and ipsec_updatereplay()
+ * from window implementation
+ *
+ * Based on RFC 6479. Blocks are 32 bits unsigned integers
+ */
+
+static inline int
+check_window(const struct secreplay *replay, uint64_t seq)
+{
+ int index, bit_location;
+
+ bit_location = seq & IPSEC_BITMAP_LOC_MASK;
+ index = (seq >> IPSEC_REDUNDANT_BIT_SHIFTS)
+ & IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size);
+
+ /* This packet already seen? */
+ return ((replay->bitmap)[index] & (1 << bit_location));
+}
+
+static inline void
+advance_window(const struct secreplay *replay, uint64_t seq)
+{
+ int i;
+ uint64_t index, index_cur, diff;
+
+ index_cur = replay->last >> IPSEC_REDUNDANT_BIT_SHIFTS;
+ index = seq >> IPSEC_REDUNDANT_BIT_SHIFTS;
+ diff = index - index_cur;
+
+ if (diff > replay->bitmap_size) {
+ /* something unusual in this case */
+ diff = replay->bitmap_size;
+ }
+
+ for (i = 0; i < diff; i++) {
+ replay->bitmap[(i + index_cur + 1)
+ & IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size)] = 0;
+ }
+}
+
+static inline void
+set_window(const struct secreplay *replay, uint64_t seq)
+{
+ int index, bit_location;
+
+ bit_location = seq & IPSEC_BITMAP_LOC_MASK;
+ index = (seq >> IPSEC_REDUNDANT_BIT_SHIFTS)
+ & IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size);
+
+ replay->bitmap[index] |= (1 << bit_location);
+}
+
/*
* Check the variable replay window.
* ipsec_chkreplay() performs replay check before ICV verification.
@@ -1184,20 +1244,17 @@
* beforehand).
* 0 (zero) is returned if packet disallowed, 1 if packet permitted.
*
- * Based on RFC 6479. Blocks are 32 bits unsigned integers
+ * Based on RFC 4303
*/
-#define IPSEC_BITMAP_INDEX_MASK(w) (w - 1)
-#define IPSEC_REDUNDANT_BIT_SHIFTS 5
-#define IPSEC_REDUNDANT_BITS (1 << IPSEC_REDUNDANT_BIT_SHIFTS)
-#define IPSEC_BITMAP_LOC_MASK (IPSEC_REDUNDANT_BITS - 1)
-
int
-ipsec_chkreplay(uint32_t seq, struct secasvar *sav)
+ipsec_chkreplay(uint32_t seq, uint32_t *seqhigh, struct secasvar *sav)
{
- const struct secreplay *replay;
- uint32_t wsizeb; /* Constant: window size. */
- int index, bit_location;
+ char buf[128];
+ struct secreplay *replay;
+ uint32_t window;
+ uint32_t tl, th, bl;
+ uint32_t seqh;
IPSEC_ASSERT(sav != NULL, ("Null SA"));
IPSEC_ASSERT(sav->replay != NULL, ("Null replay state"));
@@ -1208,36 +1265,96 @@
if (replay->wsize == 0)
return (1);
- /* Constant. */
- wsizeb = replay->wsize << 3;
+ /* Zero sequence number is not allowed. */
+ if (seq == 0 && replay->last == 0)
+ return (0);
+
+ window = replay->wsize << 3; /* Size of window */
+ tl = (uint32_t)replay->last; /* Top of window, lower part */
+ th = (uint32_t)(replay->last >> 32); /* Top of window, high part */
+ bl = tl - window + 1; /* Bottom of window, lower part */
- /* Sequence number of 0 is invalid. */
- if (seq == 0)
+ /*
+ * We keep the high part intact when:
+ * 1) the seq is within [bl, 0xffffffff] and the whole window is
+ * within one subspace;
+ * 2) the seq is within [0, bl) and window spans two subspaces.
+ */
+ if ((tl >= window - 1 && seq >= bl) ||
+ (tl < window - 1 && seq < bl)) {
+ *seqhigh = th;
+ if (seq <= tl) {
+ /* Sequence number inside window - check against replay */
+ if (check_window(replay, seq))
return (0);
+ }
- /* First time is always okay. */
- if (replay->count == 0)
+ /* Sequence number above top of window or not found in bitmap */
return (1);
+ }
- /* Larger sequences are okay. */
- if (seq > replay->lastseq)
- return (1);
+ /*
+ * If ESN is not enabled and packet with highest sequence number
+ * was received we should report overflow
+ */
+ if (tl == 0xffffffff && !(sav->flags & SADB_X_SAFLAGS_ESN)) {
+ /* Set overflow flag. */
+ replay->overflow++;
+
+ if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
+ if (sav->sah->saidx.proto == IPPROTO_ESP)
+ ESPSTAT_INC(esps_wrap);
+ else if (sav->sah->saidx.proto == IPPROTO_AH)
+ AHSTAT_INC(ahs_wrap);
+ return (0);
+ }
- /* Over range to check, i.e. too old or wrapped. */
- if (replay->lastseq - seq >= wsizeb)
+ ipseclog((LOG_WARNING, "%s: replay counter made %d cycle. %s\n",
+ __func__, replay->overflow,
+ ipsec_sa2str(sav, buf, sizeof(buf))));
+ }
+
+ /*
+ * Seq is within [bl, 0xffffffff] and bl is within
+ * [0xffffffff-window, 0xffffffff]. This means we got a seq
+ * which is within our replay window, but in the previous
+ * subspace.
+ */
+ if (tl < window - 1 && seq >= bl) {
+ if (th == 0)
return (0);
+ *seqhigh = th - 1;
+ seqh = th - 1;
+ if (check_window(replay, seq))
+ return (0);
+ return (1);
+ }
- /* The sequence is inside the sliding window
- * now check the bit in the bitmap
- * bit location only depends on the sequence number
+ /*
+ * Seq is within [0, bl) but the whole window is within one subspace.
+ * This means that seq has wrapped and is in next subspace
*/
- bit_location = seq & IPSEC_BITMAP_LOC_MASK;
- index = (seq >> IPSEC_REDUNDANT_BIT_SHIFTS)
- & IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size);
+ *seqhigh = th + 1;
+ seqh = th + 1;
- /* This packet already seen? */
- if ((replay->bitmap)[index] & (1 << bit_location))
+ /* Don't let high part to wrap */
+ if (seqh == 0) {
+ /* Set overflow flag. */
+ replay->overflow++;
+
+ if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
+ if (sav->sah->saidx.proto == IPPROTO_ESP)
+ ESPSTAT_INC(esps_wrap);
+ else if (sav->sah->saidx.proto == IPPROTO_AH)
+ AHSTAT_INC(ahs_wrap);
return (0);
+ }
+
+ ipseclog((LOG_WARNING, "%s: replay counter made %d cycle. %s\n",
+ __func__, replay->overflow,
+ ipsec_sa2str(sav, buf, sizeof(buf))));
+ }
+
return (1);
}
@@ -1249,85 +1366,90 @@
int
ipsec_updatereplay(uint32_t seq, struct secasvar *sav)
{
- char buf[128];
struct secreplay *replay;
- uint32_t wsizeb; /* Constant: window size. */
- int diff, index, bit_location;
+ uint32_t window;
+ uint32_t tl, th, bl;
+ uint32_t seqh;
IPSEC_ASSERT(sav != NULL, ("Null SA"));
IPSEC_ASSERT(sav->replay != NULL, ("Null replay state"));
replay = sav->replay;
+ /* No need to check replay if disabled. */
if (replay->wsize == 0)
- goto ok; /* No need to check replay. */
-
- /* Constant. */
- wsizeb = replay->wsize << 3;
+ return (0);
- /* Sequence number of 0 is invalid. */
- if (seq == 0)
+ /* Zero sequence number is not allowed. */
+ if (seq == 0 && replay->last == 0)
return (1);
- /* The packet is too old, no need to update */
- if (wsizeb + seq < replay->lastseq)
- goto ok;
+ window = replay->wsize << 3; /* Size of window */
+ tl = (uint32_t)replay->last; /* Top of window, lower part */
+ th = (uint32_t)(replay->last >> 32); /* Top of window, high part */
+ bl = tl - window + 1; /* Bottom of window, lower part */
- /* Now update the bit */
- index = (seq >> IPSEC_REDUNDANT_BIT_SHIFTS);
-
- /* First check if the sequence number is in the range */
- if (seq > replay->lastseq) {
- int id;
- int index_cur = replay->lastseq >> IPSEC_REDUNDANT_BIT_SHIFTS;
-
- diff = index - index_cur;
- if (diff > replay->bitmap_size) {
- /* something unusual in this case */
- diff = replay->bitmap_size;
- }
-
- for (id = 0; id < diff; ++id) {
- replay->bitmap[(id + index_cur + 1)
- & IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size)] = 0;
+ /*
+ * We keep the high part intact when:
+ * 1) the seq is within [bl, 0xffffffff] and the whole window is
+ * within one subspace;
+ * 2) the seq is within [0, bl) and window spans two subspaces.
+ */
+ if ((tl >= window - 1 && seq >= bl) ||
+ (tl < window - 1 && seq < bl)) {
+ seqh = th;
+ if (seq <= tl) {
+ /* Sequence number inside window - check against replay */
+ if (check_window(replay, seq))
+ return (1);
+ set_window(replay, seq);
+ } else {
+ advance_window(replay, ((uint64_t)seqh << 32) | seq);
+ set_window(replay, seq);
+ replay->last = ((uint64_t)seqh << 32) | seq;
}
- replay->lastseq = seq;
+ /* Sequence number above top of window or not found in bitmap */
+ replay->count++;
+ return (0);
}
- index &= IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size);
- bit_location = seq & IPSEC_BITMAP_LOC_MASK;
-
- /* this packet has already been received */
- if (replay->bitmap[index] & (1 << bit_location))
+ if (!(sav->flags & SADB_X_SAFLAGS_ESN))
return (1);
- replay->bitmap[index] |= (1 << bit_location);
+ /*
+ * Seq is within [bl, 0xffffffff] and bl is within
+ * [0xffffffff-window, 0xffffffff]. This means we got a seq
+ * which is within our replay window, but in the previous
+ * subspace.
+ */
+ if (tl < window - 1 && seq >= bl) {
+ if (th == 0)
+ return (1);
+ if (check_window(replay, seq))
+ return (1);
-ok:
- if (replay->count == ~0) {
+ set_window(replay, seq);
+ replay->count++;
+ return (0);
+ }
- /* Set overflow flag. */
- replay->overflow++;
+ /*
+ * Seq is within [0, bl) but the whole window is within one subspace.
+ * This means that seq has wrapped and is in next subspace
+ */
+ seqh = th + 1;
- /* Don't increment, no more packets accepted. */
- if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) {
- if (sav->sah->saidx.proto == IPPROTO_AH)
- AHSTAT_INC(ahs_wrap);
- else if (sav->sah->saidx.proto == IPPROTO_ESP)
- ESPSTAT_INC(esps_wrap);
+ /* Don't let high part to wrap */
+ if (seqh == 0)
return (1);
- }
-
- ipseclog((LOG_WARNING, "%s: replay counter made %d cycle. %s\n",
- __func__, replay->overflow,
- ipsec_sa2str(sav, buf, sizeof(buf))));
- }
+ advance_window(replay, ((uint64_t)seqh << 32) | seq);
+ set_window(replay, seq);
+ replay->last = ((uint64_t)seqh << 32) | seq;
replay->count++;
return (0);
}
-
int
ipsec_updateid(struct secasvar *sav, crypto_session_t *new,
crypto_session_t *old)
Index: sys/netipsec/keydb.h
===================================================================
--- sys/netipsec/keydb.h
+++ sys/netipsec/keydb.h
@@ -202,10 +202,9 @@
* (c) read only except during creation / free
*/
struct secreplay {
- u_int32_t count; /* (m) */
+ u_int64_t count; /* (m) */
u_int wsize; /* (c) window size, i.g. 4 bytes */
- u_int32_t seq; /* (m) used by sender */
- u_int32_t lastseq; /* (m) used by receiver */
+ u_int64_t last; /* (m) used by receiver */
u_int32_t *bitmap; /* (m) used by receiver */
u_int bitmap_size; /* (c) size of the bitmap array */
int overflow; /* (m) overflow flag */
Index: sys/netipsec/xform_ah.c
===================================================================
--- sys/netipsec/xform_ah.c
+++ sys/netipsec/xform_ah.c
@@ -566,6 +566,7 @@
struct newah *ah;
crypto_session_t cryptoid;
int hl, rplen, authsize, ahsize, error;
+ uint32_t seqh;
IPSEC_ASSERT(sav != NULL, ("null SA"));
IPSEC_ASSERT(sav->key_auth != NULL, ("null authentication key"));
@@ -587,7 +588,7 @@
/* Check replay window, if applicable. */
SECASVAR_LOCK(sav);
if (sav->replay != NULL && sav->replay->wsize != 0 &&
- ipsec_chkreplay(ntohl(ah->ah_seq), sav) == 0) {
+ ipsec_chkreplay(ntohl(ah->ah_seq), &seqh, sav) == 0) {
SECASVAR_UNLOCK(sav);
AHSTAT_INC(ahs_replay);
DPRINTF(("%s: packet replay failure: %s\n", __func__,
@@ -980,7 +981,7 @@
if (!V_ipsec_replay)
#endif
sav->replay->count++;
- ah->ah_seq = htonl(sav->replay->count);
+ ah->ah_seq = htonl((uint32_t)sav->replay->count);
}
cryptoid = sav->tdb_cryptoid;
SECASVAR_UNLOCK(sav);
Index: sys/netipsec/xform_esp.c
===================================================================
--- sys/netipsec/xform_esp.c
+++ sys/netipsec/xform_esp.c
@@ -295,6 +295,7 @@
uint8_t *ivp;
crypto_session_t cryptoid;
int alen, error, hlen, plen;
+ uint32_t seqh;
IPSEC_ASSERT(sav != NULL, ("null SA"));
IPSEC_ASSERT(sav->tdb_encalgxform != NULL, ("null encoding xform"));
@@ -344,7 +345,7 @@
*/
SECASVAR_LOCK(sav);
if (esph != NULL && sav->replay != NULL && sav->replay->wsize != 0) {
- if (ipsec_chkreplay(ntohl(esp->esp_seq), sav) == 0) {
+ if (ipsec_chkreplay(ntohl(esp->esp_seq), &seqh, sav) == 0) {
SECASVAR_UNLOCK(sav);
DPRINTF(("%s: packet replay check for %s\n", __func__,
ipsec_sa2str(sav, buf, sizeof(buf))));
@@ -760,7 +761,7 @@
if (!V_ipsec_replay)
#endif
sav->replay->count++;
- replay = htonl(sav->replay->count);
+ replay = htonl((uint32_t)sav->replay->count);
bcopy((caddr_t) &replay, mtod(mo, caddr_t) + roff +
sizeof(uint32_t), sizeof(uint32_t));
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Fri, Jan 31, 8:42 AM (8 h, 40 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16359001
Default Alt Text
D22367.id64314.diff (13 KB)
Attached To
Mode
D22367: Implement anti-replay algorithm with ESN support
Attached
Detach File
Event Timeline
Log In to Comment