0
0
mirror of https://github.com/OpenVPN/openvpn3.git synced 2024-09-20 04:02:15 +02:00

Added reltest.cpp for testing ReliableRecv and ReliableSend

objects by simulating an unreliable packet stream.

Modified packet_id code so that current time (now) is passed
via function calls rather than accessed as a global.

Added integer random number support via boost::random.
This commit is contained in:
James Yonan 2011-11-09 05:52:52 +00:00
parent 52c42fb5d2
commit f09b9ae12a
13 changed files with 293 additions and 75 deletions

View File

@ -0,0 +1,27 @@
#ifndef OPENVPN_COMMON_BOOSTRAND_H
#define OPENVPN_COMMON_BOOSTRAND_H
#include <boost/random.hpp>
namespace openvpn {
class RandomIntBase
{
public:
typedef unsigned int type;
RandomIntBase(type seed) : rng_(seed) {}
RandomIntBase() {} // deterministic sequence
type randrange(const type end)
{
return rng_() % end;
}
private:
boost::mt19937 rng_;
};
} // namespace openvpn
#endif // OPENVPN_COMMON_BOOSTRAND_H

View File

@ -20,6 +20,9 @@ namespace openvpn {
OPENVPN_SIMPLE_EXCEPTION(message_window_ref_by_id);
OPENVPN_SIMPLE_EXCEPTION(message_window_rm_head);
MessageWindow()
: head_id_(0), span_(0) {}
MessageWindow(const id_t starting_head_id, const id_t span)
: head_id_(starting_head_id), span_(span) {}
@ -81,6 +84,9 @@ namespace openvpn {
// would have (even if it isn't defined yet).
id_t head_id() const { return head_id_; }
// Return the id of one past the end of the window
id_t tail_id() const { return head_id_ + span_; }
// Return the window size
id_t span() const { return span_; }
@ -111,7 +117,7 @@ namespace openvpn {
// to an object in the queue
void grow(const id_t id)
{
const size_t needed_index = head_id_ + id;
const size_t needed_index = id - head_id_;
while (q_.size() <= needed_index)
q_.push_back(M());
}
@ -120,7 +126,7 @@ namespace openvpn {
// the queue, advancing the head_id_
void purge()
{
while (!q_.empty() && !q_.front().defined())
while (!q_.empty() && q_.front().erased())
{
q_.pop_front();
++head_id_;

View File

@ -21,7 +21,7 @@ namespace openvpn {
OPENVPN_SIMPLE_EXCEPTION(decrypt_hmac_verify_failed);
OPENVPN_SIMPLE_EXCEPTION(decrypt_packet_id_verify_failed);
void decrypt(BufferAllocated& buf)
void decrypt(BufferAllocated& buf, const PacketID::time_t now)
{
// verify the HMAC
if (hmac.defined())
@ -54,7 +54,7 @@ namespace openvpn {
const int cipher_mode = cipher.cipher_mode();
if (cipher_mode == CipherContext::CIPH_CBC_MODE)
{
verify_packet_id(work);
verify_packet_id(work, now);
}
else
{
@ -63,7 +63,7 @@ namespace openvpn {
}
else // no encryption
{
verify_packet_id(buf);
verify_packet_id(buf, now);
}
// return cleartext result in buf
@ -76,14 +76,14 @@ namespace openvpn {
PacketIDReceive pid_recv;
private:
void verify_packet_id(BufferAllocated& buf)
void verify_packet_id(BufferAllocated& buf, const PacketID::time_t now)
{
// ignore packet ID if pid_recv is not initialized
if (pid_recv.initialized())
{
const PacketID pid = pid_recv.read_next(buf);
if (pid_recv.test(pid)) // verify packet ID
pid_recv.add(pid); // remember packet ID
if (pid_recv.test(pid, now)) // verify packet ID
pid_recv.add(pid, now); // remember packet ID
else
throw decrypt_packet_id_verify_failed();
}

View File

@ -18,7 +18,7 @@ namespace openvpn {
public:
OPENVPN_SIMPLE_EXCEPTION(unsupported_cipher_mode);
void encrypt(BufferAllocated& buf)
void encrypt(BufferAllocated& buf, const PacketID::time_t now)
{
if (cipher.defined())
{
@ -34,7 +34,7 @@ namespace openvpn {
prng.bytes(iv_buf, iv_size);
// generate fresh outgoing packet ID and prepend to cleartext buffer
pid_send.write_next(buf, true);
pid_send.write_next(buf, true, now);
}
else
{
@ -57,7 +57,7 @@ namespace openvpn {
else // no encryption
{
// generate fresh outgoing packet ID and prepend to cleartext buffer
pid_send.write_next(buf, true);
pid_send.write_next(buf, true, now);
// HMAC the cleartext
prepend_hmac(work);

View File

@ -11,7 +11,7 @@
#include <openvpn/common/exception.hpp>
#include <openvpn/common/log.hpp>
#include <openvpn/common/circ_list.hpp>
#include <openvpn/time/now.hpp>
#include <openvpn/time/time.hpp>
#include <openvpn/buffer/buffer.hpp>
namespace openvpn {
@ -130,7 +130,7 @@ namespace openvpn {
form_ = form;
}
PacketID next()
PacketID next(const PacketID::time_t now)
{
PacketID ret;
if (!pid_.time)
@ -147,9 +147,9 @@ namespace openvpn {
return ret;
}
void write_next(Buffer& buf, const bool prepend)
void write_next(Buffer& buf, const bool prepend, const PacketID::time_t now)
{
const PacketID pid = next();
const PacketID pid = next(now);
pid.write(buf, form_, prepend);
}
@ -292,7 +292,7 @@ namespace openvpn {
* Return true if packet id is ok, or false if
* it's a replay.
*/
bool test(const PacketID& pin)
bool test(const PacketID& pin, const PacketID::time_t now)
{
// make sure we were initialized
if (!initialized_)
@ -300,12 +300,12 @@ namespace openvpn {
// see if we should do an expiration reap pass
if (last_reap_ + SEQ_REAP_PERIOD <= now)
reap();
reap(now);
// packet ID==0 is invalid
if (!pin.id)
{
debug_log (DEBUG_LOW, pin, "ID is 0", 0);
debug_log (DEBUG_LOW, pin, "ID is 0", 0, now);
return false;
}
@ -330,12 +330,12 @@ namespace openvpn {
if (diff > max_backtrack_stat_)
{
max_backtrack_stat_ = diff;
debug_log (DEBUG_LOW, pin, "UDP replay-window backtrack occurred", max_backtrack_stat_);
debug_log (DEBUG_LOW, pin, "UDP replay-window backtrack occurred", max_backtrack_stat_, now);
}
if (diff >= seq_list_.size())
{
debug_log (DEBUG_LOW, pin, "UDP large diff", diff);
debug_log (DEBUG_LOW, pin, "UDP large diff", diff, now);
return false;
}
@ -346,14 +346,14 @@ namespace openvpn {
else
{
/* raised from DEBUG_LOW to reduce verbosity */
debug_log (DEBUG_MEDIUM, pin, "UDP replay", diff);
debug_log (DEBUG_MEDIUM, pin, "UDP replay", diff, now);
return false;
}
}
}
else if (pin.time < time_) /* if time goes back, reject */
{
debug_log (DEBUG_LOW, pin, "UDP time backtrack", 0);
debug_log (DEBUG_LOW, pin, "UDP time backtrack", 0, now);
return false;
}
else /* time moved forward */
@ -373,13 +373,13 @@ namespace openvpn {
return true;
else
{
debug_log (DEBUG_MEDIUM, pin, "TCP packet ID out of sequence", 0);
debug_log (DEBUG_MEDIUM, pin, "TCP packet ID out of sequence", 0, now);
return false;
}
}
else if (pin.time < time_) /* if time goes back, reject */
{
debug_log (DEBUG_LOW, pin, "TCP time backtrack", 0);
debug_log (DEBUG_LOW, pin, "TCP time backtrack", 0, now);
return false;
}
else /* time moved forward */
@ -388,7 +388,7 @@ namespace openvpn {
return true;
else
{
debug_log (DEBUG_LOW, pin, "bad initial TCP packet ID", pin.id);
debug_log (DEBUG_LOW, pin, "bad initial TCP packet ID", pin.id, now);
return false;
}
}
@ -396,7 +396,7 @@ namespace openvpn {
}
void
add(const PacketID& pin)
add(const PacketID& pin, const PacketID::time_t now)
{
if (!initialized_)
throw packet_id_not_initialized();
@ -423,10 +423,9 @@ namespace openvpn {
// remember timestamp of packet ID
{
const PacketID::time_t local_now = now;
const size_t diff = id_ - pin.id;
if (diff < seq_list_.size() && local_now > PacketID::time_t(SEQ_EXPIRED))
seq_list_[diff] = local_now;
if (diff < seq_list_.size() && now > PacketID::time_t(SEQ_EXPIRED))
seq_list_[diff] = now;
}
}
else
@ -447,12 +446,11 @@ namespace openvpn {
}
#ifdef PACKET_ID_EXTRA_LOG_INFO
std::string str() const
std::string str(const PacketID::time_t now) const
{
if (!initialized_)
throw packet_id_not_initialized();
std::ostringstream os;
const PacketID::time_t local_now = now;
os << name_ << "-" << unit_ << " [";
for (size_t i = 0; i < seq_list_.size(); ++i)
{
@ -464,7 +462,7 @@ namespace openvpn {
c = 'E';
else
{
const int diff = int(local_now - v);
const int diff = int(now - v);
if (diff < 0)
c = 'N';
else if (diff < 10)
@ -485,9 +483,8 @@ namespace openvpn {
* be accepted because they would violate
* time_backtrack.
*/
void reap()
void reap(const PacketID::time_t now)
{
const PacketID::time_t local_now = now;
if (time_backtrack_)
{
bool expire = false;
@ -496,25 +493,25 @@ namespace openvpn {
const PacketID::time_t t = seq_list_[i];
if (t == PacketID::time_t(SEQ_EXPIRED)) // fast path -- once we see SEQ_EXPIRED from previous run, we can stop
break;
if (!expire && t && t + time_backtrack_ < local_now)
if (!expire && t && t + time_backtrack_ < now)
expire = true;
if (expire)
seq_list_[i] = PacketID::time_t(SEQ_EXPIRED);
}
}
last_reap_ = local_now;
last_reap_ = now;
}
void debug_log (const int level, const PacketID& pin, const char *description, const PacketID::id_t info) const
void debug_log (const int level, const PacketID& pin, const char *description, const PacketID::id_t info, const PacketID::time_t now) const
{
if (debug_level_ >= level)
do_log(pin, description, info);
do_log(pin, description, info, now);
}
void do_log (const PacketID& pin, const char *description, const PacketID::id_t info) const
void do_log (const PacketID& pin, const char *description, const PacketID::id_t info, const PacketID::time_t now) const
{
#ifdef PACKET_ID_EXTRA_LOG_INFO
OPENVPN_LOG("PACKET_ID: '" << description << "' pin=[" << pin.id << "," << pin.time << "] info=" << info << " state=" << str());
OPENVPN_LOG("PACKET_ID: '" << description << "' pin=[" << pin.id << "," << pin.time << "] info=" << info << " state=" << str(now));
#else
OPENVPN_LOG("PACKET_ID: '" << description << "' pin=[" << pin.id << "," << pin.time << "] info=" << info << " state=" << name_ << "-" << unit_);
#endif

View File

@ -4,6 +4,7 @@
#include <openssl/rand.h>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/boostrand.hpp>
namespace openvpn {
OPENVPN_SIMPLE_EXCEPTION(rand_error);
@ -14,6 +15,20 @@ namespace openvpn {
throw rand_error();
}
template <typename T>
inline T rand_type()
{
T ret;
rand_bytes((unsigned char *)&ret, sizeof(ret));
return ret;
}
class RandomInt : public RandomIntBase
{
public:
RandomInt() : RandomIntBase(rand_type<unsigned int>()) {}
};
} // namespace openvpn
#endif // OPENVPN_OPENSSL_RAND_H

View File

@ -0,0 +1,36 @@
#ifndef OPENVPN_RELIABLE_RELCOMMON_H
#define OPENVPN_RELIABLE_RELCOMMON_H
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/crypto/packet_id.hpp>
namespace openvpn {
class ReliableMessageBase
{
public:
typedef PacketID::id_t id_t;
ReliableMessageBase() : id_(0), erased_(false) {}
bool defined() const { return bool(buffer); }
bool erased() const { return erased_; }
void erase()
{
id_ = id_t(0);
buffer.reset();
erased_ = true;
}
id_t id() const { return id_; }
BufferPtr buffer;
protected:
id_t id_;
bool erased_;
};
} // namespace openvpn
#endif // OPENVPN_RELIABLE_RELCOMMON_H

View File

@ -5,62 +5,65 @@
#include <openvpn/common/exception.hpp>
#include <openvpn/common/msgwin.hpp>
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/crypto/packet_id.hpp>
#include <openvpn/reliable/relcommon.hpp>
namespace openvpn {
class ReliableRecv
{
public:
typedef PacketID::id_t id_t;
OPENVPN_SIMPLE_EXCEPTION(rel_next_sequenced_not_ready);
OPENVPN_SIMPLE_EXCEPTION(next_sequenced_not_ready);
typedef ReliableMessageBase::id_t id_t;
struct Message
class Message : public ReliableMessageBase
{
public:
Message() : id(0) {}
bool defined() const { return bool(buffer); }
void erase() { buffer.reset(); }
id_t id;
BufferPtr buffer;
friend class ReliableRecv;
};
ReliableRecv() {}
ReliableRecv(const id_t span) { init(span); }
void init(const id_t span)
{
window_.init(1, span);
}
// Call with unsequenced packet off of the wire.
// Will return true if ACK for this packet ID
// should be returned to sender.
bool receive(BufferPtr& buffer, const id_t id)
{
if (window.in_window(id))
if (window_.in_window(id))
{
Message& m = window.ref_by_id(id);
m.id = id;
Message& m = window_.ref_by_id(id);
m.id_ = id;
m.buffer = buffer;
return true;
}
else
return window.pre_window(id);
return window_.pre_window(id);
}
// Return true if next_sequenced() is ready to return next buffer
bool ready() const { return window.head_defined(); }
bool ready() const { return window_.head_defined(); }
// Return next buffer in sequence. Requires that ready() returns true.
Message next_sequenced()
// Return next buffer in sequence.
// Requires that ready() returns true.
Message& next_sequenced()
{
if (ready())
{
Message ret = window.ref_head();
window.rm_head_nocheck();
return ret;
}
else
throw next_sequenced_not_ready();
return window_.ref_head();
}
// Call after buffer returned by receive is ready to
// be disposed of.
void advance()
{
window_.rm_head_nocheck();
}
private:
MessageWindow<Message, id_t> window;
MessageWindow<Message, id_t> window_;
};
} // namespace openvpn

View File

@ -0,0 +1,116 @@
#ifndef OPENVPN_RELIABLE_RELSEND_H
#define OPENVPN_RELIABLE_RELSEND_H
#include <openvpn/common/types.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/msgwin.hpp>
#include <openvpn/time/time.hpp>
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/reliable/relcommon.hpp>
namespace openvpn {
class ReliableSend
{
public:
typedef ReliableMessageBase::id_t id_t;
enum {
RETRANSMIT = 2 // retransmit in N seconds if ACK not received
};
class Message : public ReliableMessageBase
{
friend class ReliableSend;
public:
bool ready_retransmit(const Time now) const
{
return defined() && now >= retransmit_at_;
}
Time::Duration until_retransmit(const Time now) const
{
Time::Duration ret;
if (now < retransmit_at_)
ret = retransmit_at_ - now;
return ret;
}
void reset_retransmit(const Time now)
{
retransmit_at_ = now + Time::Duration::seconds(RETRANSMIT);
}
private:
Time retransmit_at_;
};
ReliableSend() : next(0) {}
ReliableSend(const id_t span) { init(span); }
void init(const id_t span)
{
next = 1;
window_.init(next, span);
}
// Return the id that the object at the head of the queue
// would have (even if it isn't defined yet).
id_t head_id() const { return window_.head_id(); }
// Return the ID of one past the end of the window
id_t tail_id() const { return window_.tail_id(); }
// Return the window size
id_t span() const { return window_.span(); }
// Return a reference to M object at id, throw exception
// if id is not in current window
Message& ref_by_id(const id_t id)
{
return window_.ref_by_id(id);
}
// Return the shortest duration for any pending retransmissions
Time::Duration until_retransmit(const Time now)
{
Time::Duration ret = Time::Duration::infinite();
for (id_t i = head_id(); i < tail_id(); ++i)
{
Message& msg = ref_by_id(i);
if (msg.defined())
{
Time::Duration ut = msg.until_retransmit(now);
if (ut < ret)
ret = ut;
}
}
return ret;
}
// Return a fresh Message object that can be used to
// construct the next packet in the sequence. Don't call
// unless ready() returns true.
Message& send(const Time now)
{
Message& msg = window_.ref_by_id(next);
msg.id_ = next++;
msg.reset_retransmit(now);
return msg;
}
// Return true if output buffer is ready to receive another packet
bool ready() const { return window_.in_window(next); }
// Remove a packet from send buffer that has been acknowledged
void ack(const id_t id) { window_.rm_by_id(id); }
private:
id_t next;
MessageWindow<Message, id_t> window_;
};
} // namespace openvpn
#endif // OPENVPN_RELIABLE_RELSEND_H

View File

@ -4,9 +4,11 @@
#include <openvpn/time/time.hpp>
namespace openvpn {
namespace time {
volatile Time::base_type now = 0; /* GLOBAL */
Time now; /* GLOBAL */ // fixme should be volatile
} // namespace time
} // namespace openvpn
#endif // OPENVPN_COMMON_NOW_H

View File

@ -33,8 +33,8 @@ namespace openvpn {
const Time local_now = Time::now();
const Time::type fs = local_now.fractional_binary_ms();
const Time::Duration next_second = Time::Duration::binary_ms(Time::prec - fs);
now = local_now.seconds_since_epoch();
//std::cout << now << std::endl;
time::now = local_now;
//std::cout << time::now.seconds_since_epoch() << std::endl;
timer_.expires_at(local_now + next_second);
timer_.async_wait(asio_dispatch_timer(&NowUpdater::timer_callback, this));
}

View File

@ -1,6 +1,8 @@
#ifndef OPENVPN_COMMON_TIME_H
#define OPENVPN_COMMON_TIME_H
#include <limits>
#include <sys/time.h>
#include <openvpn/common/exception.hpp>
@ -23,11 +25,13 @@ namespace openvpn {
public:
static Duration seconds(const T v) { return Duration(v * prec); }
static Duration binary_ms(const T v) { return Duration(v); }
static Duration infinite() { return Duration(std::numeric_limits<T>::max()); }
Duration() : duration_(T(0)) {}
operator bool() const { return duration_ != 0; }
bool operator!() const { return duration_ == 0; }
bool defined() const { return duration_ != T(0); }
bool operator!() const { return duration_ == T(0); }
bool is_infinite() const { return duration_ == std::numeric_limits<T>::max(); }
Duration operator+(const Duration& d) const { return Duration(duration_ + d.duration_); }
Duration operator-(const Duration& d) const { return Duration(duration_ - d.duration_); }
@ -42,6 +46,8 @@ namespace openvpn {
T to_binary_ms() const { return duration_; }
T to_microseconds() const { return duration_ * 1000000 / prec; }
T raw() const { return duration_; }
# define OPENVPN_DURATION_REL(OP) bool operator OP(const Duration& d) const { return duration_ OP d.duration_; }
OPENVPN_DURATION_REL(==)
OPENVPN_DURATION_REL(!=)
@ -59,7 +65,10 @@ namespace openvpn {
TimeType() : time_(T(0)) {}
operator bool() const { return time_ != 0; }
static TimeType zero() { return TimeType(T(0)); }
static TimeType infinite() { return TimeType(std::numeric_limits<T>::max()); }
bool defined() const { return time_ != 0; }
bool operator!() const { return time_ == 0; }
base_type seconds_since_epoch() const { return base_ + time_ / prec; }
@ -76,6 +85,8 @@ namespace openvpn {
static void reset_base() { base_ = ::time(0); }
TimeType operator+(const Duration& d) const { return TimeType(time_ + d.duration_); }
TimeType& operator+=(const Duration& d) { time_ += d.duration_; return *this; }
Duration operator-(const TimeType& t) const { return Duration(time_ - t.time_); }
# define OPENVPN_TIME_REL(OP) bool operator OP(const TimeType& t) const { return time_ OP t.time_; }
@ -87,6 +98,8 @@ namespace openvpn {
OPENVPN_TIME_REL(<=)
# undef OPENVPN_TIME_REL
T raw() const { return time_; }
private:
explicit TimeType(const T time) : time_(time) {}
@ -96,7 +109,7 @@ namespace openvpn {
template <typename T> typename TimeType<T>::base_type TimeType<T>::base_;
typedef TimeType<unsigned int> Time;
typedef TimeType<unsigned long> Time;
} // namespace openvpn

View File

@ -5,6 +5,7 @@ if [ -z "$1" ]; then
echo " DEBUG=1 -- enable debug"
echo " STRIP=1 -- strip binary"
echo " LTO=1 -- build with LTO"
echo " GPROF=1 -- build for gprof profiling"
exit 1
fi
@ -14,9 +15,11 @@ if [ "$DEBUG" ]; then
else
# release build
FLAGS=""
[ "$LTO" ] && FLAGS="-flto=4 -Wl,--no-as-needed "
[ "$LTO" ] && FLAGS="$FLAGS -flto=4 -Wl,--no-as-needed"
[ "$GPROF" ] && FLAGS="$FLAGS -pg"
g++ -Wall -O3 -fwhole-program $FLAGS -pthread $GCC_EXTRA -I$BOOST -I$OVPN3 -L$BOOST/stage/lib $1.cpp -o $1 -lboost_system -lcrypto -lssl
fi
# maybe strip
[ "$STRIP" ] && strip $1
exit 0