Changeset View
Changeset View
Standalone View
Standalone View
head/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. | |||||
* These are used to separate 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. | * 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 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); | 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 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 |