/* vim: set expandtab ts=4 sw=4: */
/*
* You may redistribute this program and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifndef ReplayProtector_H
#define ReplayProtector_H
#include "util/Bits.h"
#include
struct ReplayProtector
{
/** internal bitfield. */
uint64_t bitfield;
/** Internal offset. */
uint32_t baseOffset;
/** Number of definite duplicate packets. */
uint32_t duplicates;
/** Number of lost packets. */
uint32_t lostPackets;
/**
* Number of packets which could not be verified because they were out of range.
* Growing lostPackets and receivedOutOfRange together indicate severe packet
* reordering issues.
* receivedOutOfRange growing along indicates duplicate packets.
*/
uint32_t receivedOutOfRange;
};
static inline int RelayProtector_lostInShift(uint64_t bitfield, int shiftAmount)
{
if (shiftAmount == 0) {
return 0;
}
if (shiftAmount > 63) {
return shiftAmount - Bits_popCountx64(bitfield);
}
return shiftAmount - Bits_popCountx64(bitfield << (64 - shiftAmount));
}
/**
* Check a nonce and file it as being seen.
* Don't call this until the packet has been authenticated or else forged packets will
* make legit ones appear to be duplicates.
*
* @param nonce the number to check, this should be a counter nonce as numbers less than 32 minus
* the highest seen nonce will be dropped erroniously.
* @param context the context
* @return true if the packet is provably not a replay, otherwise false.
*/
static inline bool ReplayProtector_checkNonce(const uint32_t nonce, struct ReplayProtector* context)
{
if (nonce < context->baseOffset) {
context->receivedOutOfRange++;
return false;
}
uint32_t offset = nonce - context->baseOffset;
while (offset > 63) {
#define ReplayProtector_DO_SHIFT(bits) \
context->baseOffset += (bits); \
if ((bits) > 63) { \
context->bitfield = 0; \
} else { \
context->bitfield >>= (bits); \
} \
offset -= (bits);
if ((context->bitfield & 0xffffffffu) == 0xffffffffu) {
// happy path, low 32 bits are checked in, rotate and continue.
ReplayProtector_DO_SHIFT(32);
} else {
// we are going to have to accept some losses, take offset - 47 to mitigate that
// as much as possible.
context->lostPackets += RelayProtector_lostInShift(context->bitfield, offset - 47);
ReplayProtector_DO_SHIFT(offset - 47);
}
}
if (context->bitfield & (((uint64_t)1) << offset)) {
context->duplicates++;
return false;
}
context->bitfield |= (((uint64_t)1) << offset);
return true;
}
#endif