Changeset View
Changeset View
Standalone View
Standalone View
sys/netipsec/ipsec.c
Show First 20 Lines • Show All 1,167 Lines • ▼ Show 20 Lines | ipsec_hdrsiz_inpcb(struct inpcb *inp) | |||||||||
} | } | |||||||||
if (sp == NULL) | if (sp == NULL) | |||||||||
sp = key_allocsp_default(); | sp = key_allocsp_default(); | |||||||||
sz = ipsec_hdrsiz_internal(sp); | sz = ipsec_hdrsiz_internal(sp); | |||||||||
key_freesp(&sp); | key_freesp(&sp); | |||||||||
return (sz); | 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() | ||||||||||
jhbUnsubmitted Done Inline Actions
jhb: | ||||||||||
* 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. | * Check the variable replay window. | |||||||||
* ipsec_chkreplay() performs replay check before ICV verification. | * ipsec_chkreplay() performs replay check before ICV verification. | |||||||||
* ipsec_updatereplay() updates replay bitmap. This must be called after | * ipsec_updatereplay() updates replay bitmap. This must be called after | |||||||||
* ICV verification (it also performs replay check, which is usually done | * ICV verification (it also performs replay check, which is usually done | |||||||||
* beforehand). | * beforehand). | |||||||||
* 0 (zero) is returned if packet disallowed, 1 if packet permitted. | * 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 | int | |||||||||
ipsec_chkreplay(uint32_t seq, struct secasvar *sav) | ipsec_chkreplay(uint32_t seq, uint32_t *seqhigh, struct secasvar *sav) | |||||||||
{ | { | |||||||||
const struct secreplay *replay; | char buf[128]; | |||||||||
uint32_t wsizeb; /* Constant: window size. */ | struct secreplay *replay; | |||||||||
int index, bit_location; | uint32_t window; | |||||||||
uint32_t tl, th, bl; | ||||||||||
uint32_t seqh; | ||||||||||
IPSEC_ASSERT(sav != NULL, ("Null SA")); | IPSEC_ASSERT(sav != NULL, ("Null SA")); | |||||||||
IPSEC_ASSERT(sav->replay != NULL, ("Null replay state")); | IPSEC_ASSERT(sav->replay != NULL, ("Null replay state")); | |||||||||
replay = sav->replay; | replay = sav->replay; | |||||||||
/* No need to check replay if disabled. */ | /* No need to check replay if disabled. */ | |||||||||
if (replay->wsize == 0) | if (replay->wsize == 0) | |||||||||
return (1); | return (1); | |||||||||
/* Constant. */ | /* Zero sequence number is not allowed. */ | |||||||||
wsizeb = replay->wsize << 3; | if (seq == 0 && replay->last == 0) | |||||||||
return (0); | ||||||||||
/* Sequence number of 0 is invalid. */ | window = replay->wsize << 3; /* Size of window */ | |||||||||
if (seq == 0) | 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 */ | ||||||||||
/* | ||||||||||
* 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); | return (0); | |||||||||
} | ||||||||||
/* First time is always okay. */ | /* Sequence number above top of window or not found in bitmap */ | |||||||||
if (replay->count == 0) | ||||||||||
return (1); | return (1); | |||||||||
} | ||||||||||
/* Larger sequences are okay. */ | /* | |||||||||
if (seq > replay->lastseq) | * If ESN is not enabled and packet with highest sequence number | |||||||||
return (1); | * was received we should report overflow | |||||||||
*/ | ||||||||||
if (tl == 0xffffffff && !(sav->flags & SADB_X_SAFLAGS_ESN)) { | ||||||||||
/* Set overflow flag. */ | ||||||||||
replay->overflow++; | ||||||||||
/* Over range to check, i.e. too old or wrapped. */ | if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) { | |||||||||
if (replay->lastseq - seq >= wsizeb) | 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); | return (0); | |||||||||
} | ||||||||||
/* The sequence is inside the sliding window | ipseclog((LOG_WARNING, "%s: replay counter made %d cycle. %s\n", | |||||||||
* now check the bit in the bitmap | __func__, replay->overflow, | |||||||||
* bit location only depends on the sequence number | 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. | ||||||||||
*/ | */ | |||||||||
bit_location = seq & IPSEC_BITMAP_LOC_MASK; | if (tl < window - 1 && seq >= bl) { | |||||||||
index = (seq >> IPSEC_REDUNDANT_BIT_SHIFTS) | if (th == 0) | |||||||||
& IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size); | return (0); | |||||||||
*seqhigh = th - 1; | ||||||||||
seqh = th - 1; | ||||||||||
if (check_window(replay, seq)) | ||||||||||
return (0); | ||||||||||
return (1); | ||||||||||
} | ||||||||||
/* This packet already seen? */ | /* | |||||||||
if ((replay->bitmap)[index] & (1 << bit_location)) | * Seq is within [0, bl) but the whole window is within one subspace. | |||||||||
* This means that seq has wrapped and is in next subspace | ||||||||||
*/ | ||||||||||
*seqhigh = th + 1; | ||||||||||
seqh = th + 1; | ||||||||||
/* Don't let high part to wrap */ | ||||||||||
jhbUnsubmitted Done Inline Actions
jhb: | ||||||||||
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); | return (0); | |||||||||
} | ||||||||||
ipseclog((LOG_WARNING, "%s: replay counter made %d cycle. %s\n", | ||||||||||
__func__, replay->overflow, | ||||||||||
ipsec_sa2str(sav, buf, sizeof(buf)))); | ||||||||||
} | ||||||||||
return (1); | return (1); | |||||||||
} | } | |||||||||
/* | /* | |||||||||
* Check replay counter whether to update or not. | * Check replay counter whether to update or not. | |||||||||
* OUT: 0: OK | * OUT: 0: OK | |||||||||
* 1: NG | * 1: NG | |||||||||
*/ | */ | |||||||||
int | int | |||||||||
ipsec_updatereplay(uint32_t seq, struct secasvar *sav) | ipsec_updatereplay(uint32_t seq, struct secasvar *sav) | |||||||||
{ | { | |||||||||
char buf[128]; | ||||||||||
struct secreplay *replay; | struct secreplay *replay; | |||||||||
uint32_t wsizeb; /* Constant: window size. */ | uint32_t window; | |||||||||
int diff, index, bit_location; | uint32_t tl, th, bl; | |||||||||
uint32_t seqh; | ||||||||||
IPSEC_ASSERT(sav != NULL, ("Null SA")); | IPSEC_ASSERT(sav != NULL, ("Null SA")); | |||||||||
IPSEC_ASSERT(sav->replay != NULL, ("Null replay state")); | IPSEC_ASSERT(sav->replay != NULL, ("Null replay state")); | |||||||||
replay = sav->replay; | replay = sav->replay; | |||||||||
/* No need to check replay if disabled. */ | ||||||||||
if (replay->wsize == 0) | if (replay->wsize == 0) | |||||||||
goto ok; /* No need to check replay. */ | return (0); | |||||||||
/* Constant. */ | /* Zero sequence number is not allowed. */ | |||||||||
wsizeb = replay->wsize << 3; | if (seq == 0 && replay->last == 0) | |||||||||
/* Sequence number of 0 is invalid. */ | ||||||||||
if (seq == 0) | ||||||||||
return (1); | return (1); | |||||||||
/* The packet is too old, no need to update */ | window = replay->wsize << 3; /* Size of window */ | |||||||||
if (wsizeb + seq < replay->lastseq) | tl = (uint32_t)replay->last; /* Top of window, lower part */ | |||||||||
goto ok; | 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); | * We keep the high part intact when: | |||||||||
* 1) the seq is within [bl, 0xffffffff] and the whole window is | ||||||||||
/* First check if the sequence number is in the range */ | * within one subspace; | |||||||||
if (seq > replay->lastseq) { | * 2) the seq is within [0, bl) and window spans two subspaces. | |||||||||
int id; | */ | |||||||||
int index_cur = replay->lastseq >> IPSEC_REDUNDANT_BIT_SHIFTS; | if ((tl >= window - 1 && seq >= bl) || | |||||||||
(tl < window - 1 && seq < bl)) { | ||||||||||
diff = index - index_cur; | seqh = th; | |||||||||
if (diff > replay->bitmap_size) { | if (seq <= tl) { | |||||||||
/* something unusual in this case */ | /* Sequence number inside window - check against replay */ | |||||||||
diff = replay->bitmap_size; | 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; | ||||||||||
} | } | |||||||||
for (id = 0; id < diff; ++id) { | /* Sequence number above top of window or not found in bitmap */ | |||||||||
replay->bitmap[(id + index_cur + 1) | replay->count++; | |||||||||
& IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size)] = 0; | return (0); | |||||||||
} | } | |||||||||
replay->lastseq = seq; | if (!(sav->flags & SADB_X_SAFLAGS_ESN)) | |||||||||
} | return (1); | |||||||||
index &= IPSEC_BITMAP_INDEX_MASK(replay->bitmap_size); | /* | |||||||||
bit_location = seq & IPSEC_BITMAP_LOC_MASK; | * Seq is within [bl, 0xffffffff] and bl is within | |||||||||
* [0xffffffff-window, 0xffffffff]. This means we got a seq | ||||||||||
/* this packet has already been received */ | * which is within our replay window, but in the previous | |||||||||
if (replay->bitmap[index] & (1 << bit_location)) | * subspace. | |||||||||
*/ | ||||||||||
if (tl < window - 1 && seq >= bl) { | ||||||||||
if (th == 0) | ||||||||||
return (1); | return (1); | |||||||||
if (check_window(replay, seq)) | ||||||||||
return (1); | ||||||||||
replay->bitmap[index] |= (1 << bit_location); | set_window(replay, seq); | |||||||||
replay->count++; | ||||||||||
return (0); | ||||||||||
} | ||||||||||
ok: | /* | |||||||||
if (replay->count == ~0) { | * Seq is within [0, bl) but the whole window is within one subspace. | |||||||||
/* Set overflow flag. */ | * This means that seq has wrapped and is in next subspace | |||||||||
replay->overflow++; | */ | |||||||||
seqh = th + 1; | ||||||||||
/* Don't increment, no more packets accepted. */ | /* Don't let high part to wrap */ | |||||||||
if ((sav->flags & SADB_X_EXT_CYCSEQ) == 0) { | if (seqh == 0) | |||||||||
if (sav->sah->saidx.proto == IPPROTO_AH) | ||||||||||
AHSTAT_INC(ahs_wrap); | ||||||||||
else if (sav->sah->saidx.proto == IPPROTO_ESP) | ||||||||||
ESPSTAT_INC(esps_wrap); | ||||||||||
return (1); | return (1); | |||||||||
} | ||||||||||
ipseclog((LOG_WARNING, "%s: replay counter made %d cycle. %s\n", | advance_window(replay, ((uint64_t)seqh << 32) | seq); | |||||||||
__func__, replay->overflow, | set_window(replay, seq); | |||||||||
ipsec_sa2str(sav, buf, sizeof(buf)))); | replay->last = ((uint64_t)seqh << 32) | seq; | |||||||||
} | ||||||||||
replay->count++; | replay->count++; | |||||||||
return (0); | return (0); | |||||||||
} | } | |||||||||
int | int | |||||||||
ipsec_updateid(struct secasvar *sav, crypto_session_t *new, | ipsec_updateid(struct secasvar *sav, crypto_session_t *new, | |||||||||
crypto_session_t *old) | crypto_session_t *old) | |||||||||
{ | { | |||||||||
crypto_session_t tmp; | crypto_session_t tmp; | |||||||||
/* | /* | |||||||||
* tdb_cryptoid is initialized by xform_init(). | * tdb_cryptoid is initialized by xform_init(). | |||||||||
▲ Show 20 Lines • Show All 73 Lines • Show Last 20 Lines |