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

ProtoContext object can now be reused via reset() method.

Fixed rare bug where client receives auth, goes ACTIVE, but the ACK
response back to the server is dropped causing the server to receive
post-ACTIVE app messages from the client while it's still stuck
in the S_WAIT_AUTH_ACK state.
This commit is contained in:
James Yonan 2011-12-17 10:53:21 +00:00
parent 6260957c37
commit fb163b65f4
5 changed files with 141 additions and 43 deletions

View File

@ -977,6 +977,7 @@ namespace openvpn {
send_auth();
state = S_WAIT_AUTH_ACK;
break;
case S_WAIT_AUTH_ACK: // rare case where client receives auth, goes ACTIVE, but the ACK response is dropped
case ACTIVE:
proto.app_recv(key_id_, to_app_buf);
break;
@ -1032,6 +1033,7 @@ namespace openvpn {
write_auth_string(peer_info, *buf);
}
Base::app_send(buf);
dirty = true;
}
void recv_auth(BufferAllocated& buf)
@ -1059,6 +1061,7 @@ namespace openvpn {
{
Base::app_send(app_pre_write_queue.front());
app_pre_write_queue.pop_front();
dirty = true;
}
reached_active_time_ = *now;
proto.slowest_handshake_.max(reached_active_time_ - construct_time);
@ -1370,9 +1373,7 @@ namespace openvpn {
const ProtoStats::Ptr& stats_arg) // error stats
: config(config_arg),
stats(stats_arg),
hmac_size(0),
use_tls_auth(false),
upcoming_key_id(0),
server_(config_arg->ssl_ctx->mode() == SSLConfig::SERVER),
n_key_ids(0),
now_(config_arg->now),
keepalive_ping(config_arg->keepalive_ping),
@ -1380,9 +1381,6 @@ namespace openvpn {
{
const Config& c = *config;
// determine client/server status
server_ = (c.ssl_ctx->mode() == SSLConfig::SERVER);
// tls-auth setup
if (c.tls_auth_key.defined())
{
@ -1390,7 +1388,29 @@ namespace openvpn {
// get HMAC size from Digest object
hmac_size = c.tls_auth_digest.size();
}
else
{
use_tls_auth = false;
hmac_size = 0;
}
reset();
}
void reset()
{
const Config& c = *config;
// clear key contexts
primary.reset();
secondary.reset();
// start with key ID 0
upcoming_key_id = 0;
// tls-auth initialization
if (use_tls_auth)
{
// init tls_auth hmac
const unsigned int key_dir = server_ ? OpenVPNStaticKey::NORMAL : OpenVPNStaticKey::INVERSE;
ta_hmac_send.init(c.tls_auth_digest, c.tls_auth_key.slice(OpenVPNStaticKey::HMAC | OpenVPNStaticKey::ENCRYPT | key_dir));
@ -1408,8 +1428,9 @@ namespace openvpn {
// initialize proto session ID
psid_self.randomize(*c.prng);
psid_peer.reset();
// initialize primary key context
// initialize key contexts
primary.reset(new KeyContext(*this, !server_));
// initialize keepalive timers
@ -1755,6 +1776,7 @@ namespace openvpn {
switch (ev)
{
case KeyContext::KEV_ACTIVE:
OPENVPN_LOG_PROTO("*** SESSION_ACTIVE");
active();
break;
case KeyContext::KEV_RENEGOTIATE:

View File

@ -18,8 +18,13 @@ namespace openvpn {
};
ProtoSessionID()
: defined_(false)
{
reset();
}
void reset()
{
defined_ = false;
std::memset(id_, 0, SIZE);
}

View File

@ -7,6 +7,8 @@ if [ -z "$1" ]; then
echo " STRIP=1 -- strip binary"
echo " LTO=1 -- build with LTO"
echo " GPROF=1 -- build for gprof profiling"
echo " PGEN=1 -- generate data for profile-guided optimization"
echo " PUSE=1 -- use data from previous run of PGEN=1 build"
echo " OSSL=1 -- include OpenSSL on Mac"
echo " SSL_BOTH=1 -- include OpenSSL and Apple SSL on Mac"
echo " NOTHREADS=1 -- disable threads"
@ -17,6 +19,9 @@ fi
# remove previous build
rm -f $1
# build flags
FLAGS=""
# special Mac settings
if [ "$PLATFORM" = "mac" ]; then
# On Mac, only link with OpenSSL if OSSL is defined.
@ -41,21 +46,27 @@ fi
# boost link libraries
BOOST_LIBS="-lboost_system"
if [ "$NOTHREADS" = "1" ]; then
THREAD_FLAGS="-DBOOST_DISABLE_THREADS"
FLAGS="$FLAGS -DBOOST_DISABLE_THREADS"
else
BOOST_LIBS="$BOOST_LIBS -lboost_thread"
THREAD_FLAGS="-pthread"
FLAGS="$FLAGS -pthread"
fi
# profile-guided optimization
if [ "$PGEN" = "1" ]; then
FLAGS="$FLAGS -fprofile-generate"
elif [ "$PUSE" = "1" ]; then
FLAGS="$FLAGS -fprofile-use"
fi
if [ "$DEBUG" = "1" ]; then
# debug build
CMD="g++ -Wall -g $THREAD_FLAGS $GCC_EXTRA -I$BOOST -I$OVPN3 -L$BOOST/stage/lib $1.cpp -o $1 $BOOST_LIBS $CRYPTO_LIBS"
CMD="g++ -Wall -g $FLAGS $GCC_EXTRA -I$BOOST -I$OVPN3 -L$BOOST/stage/lib $1.cpp -o $1 $BOOST_LIBS $CRYPTO_LIBS"
else
# release build
FLAGS=""
[ "$LTO" = "1" ] && FLAGS="$FLAGS -flto=4 -Wl,--no-as-needed"
[ "$GPROF" = "1" ] && FLAGS="$FLAGS -pg"
CMD="g++ -Wall -O3 -fwhole-program $FLAGS $THREAD_FLAGS $GCC_EXTRA -I$BOOST -I$OVPN3 -L$BOOST/stage/lib $1.cpp -o $1 $BOOST_LIBS $CRYPTO_LIBS"
CMD="g++ -Wall -O3 -fwhole-program $FLAGS $GCC_EXTRA -I$BOOST -I$OVPN3 -L$BOOST/stage/lib $1.cpp -o $1 $BOOST_LIBS $CRYPTO_LIBS"
fi
# execute CMD

View File

@ -21,6 +21,14 @@
#define ITER 1000000
#endif
// number of high-level session iterations
#ifndef SITER
#define SITER 1
#endif
// abort if we reach this limit
//#define DROUGHT_LIMIT 100000
#if !defined(VERBOSE) && ITER <= 10000
#define VERBOSE
#endif
@ -91,8 +99,10 @@ const char message[] =
class DroughtMeasure
{
public:
DroughtMeasure(TimePtr now_arg)
: now(now_arg)
OPENVPN_SIMPLE_EXCEPTION(drought_limit_exceeded);
DroughtMeasure(const std::string& name_arg, TimePtr now_arg)
: now(now_arg), name(name_arg)
{
}
@ -102,7 +112,21 @@ public:
{
Time::Duration since_last = *now - last_event;
if (since_last > drought)
drought = since_last;
{
drought = since_last;
#if defined(VERBOSE) || defined(DROUGHT_LIMIT)
{
const unsigned int r = drought.raw();
#ifdef VERBOSE
std::cout << "*** Drought " << name << " has reached " << r << std::endl;
#endif
#ifdef DROUGHT_LIMIT
if (r > DROUGHT_LIMIT)
throw drought_limit_exceeded();
#endif
}
#endif
}
}
last_event = *now;
}
@ -113,6 +137,7 @@ private:
TimePtr now;
Time last_event;
Time::Duration drought;
std::string name;
};
// test the OpenVPN protocol implementation in ProtoContext
@ -121,7 +146,6 @@ class TestProto : public ProtoContext<SSL_CONTEXT>
{
typedef ProtoContext<SSL_CONTEXT> Base;
using Base::start;
using Base::now;
using Base::server;
@ -133,8 +157,8 @@ public:
TestProto(const typename Base::Config::Ptr& config,
const ProtoStats::Ptr& stats)
: Base(config, stats),
control_drought(config->now),
data_drought(config->now),
control_drought("control", config->now),
data_drought("data", config->now),
frame(config->frame),
app_bytes_(0),
net_bytes_(0),
@ -144,6 +168,23 @@ public:
std::memset(progress_, 0, 11);
}
void reset()
{
net_out.clear();
Base::reset();
}
void initial_app_send(const char *msg)
{
Base::start();
const size_t msglen = std::strlen(msg);
BufferAllocated app_buf((unsigned char *)msg, msglen, 0);
copy_progress(app_buf);
control_send(app_buf);
flush(true);
}
bool do_housekeeping()
{
if (now() >= Base::next_housekeeping())
@ -191,16 +232,6 @@ public:
}
}
void initial_app_send(const char *msg)
{
start();
const size_t msglen = std::strlen(msg);
BufferAllocated app_buf((unsigned char *)msg, msglen, 0);
control_send(app_buf);
flush(true);
}
size_t net_bytes() const { return net_bytes_; }
size_t app_bytes() const { return app_bytes_; }
size_t data_bytes() const { return data_bytes_; }
@ -244,6 +275,12 @@ private:
control_drought.event();
}
void copy_progress(Buffer& buf)
{
if (progress_[0]) // make sure progress was initialized
std::memcpy(buf.data()+13, progress_, 10);
}
void modmsg(BufferPtr& buf)
{
char *msg = (char *) buf->data();
@ -551,7 +588,11 @@ void test(const int thread_num)
cp->pid_seq_backtrack = 64;
cp->pid_time_backtrack = 30;
cp->pid_debug_level = PacketIDReceive::DEBUG_QUIET;
#if SITER > 1
cp->handshake_window = Time::Duration::seconds(30);
#else
cp->handshake_window = Time::Duration::seconds(18); // will cause a small number of handshake failures
#endif
cp->become_primary = Time::Duration::seconds(30);
cp->renegotiate = Time::Duration::seconds(95);
cp->expire = Time::Duration::seconds(150);
@ -599,7 +640,11 @@ void test(const int thread_num)
sp->pid_seq_backtrack = 64;
sp->pid_time_backtrack = 30;
sp->pid_debug_level = PacketIDReceive::DEBUG_QUIET;
#if SITER > 1
sp->handshake_window = Time::Duration::seconds(30);
#else
sp->handshake_window = Time::Duration::seconds(17) + Time::Duration::binary_ms(512);
#endif
sp->become_primary = Time::Duration::seconds(30);
sp->renegotiate = Time::Duration::seconds(90);
sp->expire = Time::Duration::seconds(150);
@ -617,18 +662,29 @@ void test(const int thread_num)
TestProtoClient<ClientSSLContext> cli_proto(cp, cli_stats);
TestProtoServer<OpenSSLContext> serv_proto(sp, serv_stats);
NoisyWire client_to_server("Client -> Server", &time, rand, 8, 16, 32); // last value: 32
NoisyWire server_to_client("Server -> Client", &time, rand, 8, 16, 32); // last value: 32
// start feedback loop
cli_proto.initial_app_send(message);
// message loop
for (int j = 0; j < ITER; ++j)
for (int i = 0; i < SITER; ++i)
{
client_to_server.xfer(cli_proto, serv_proto);
server_to_client.xfer(serv_proto, cli_proto);
time += time_step;
#ifdef VERBOSE
std::cout << "***** SITER " << i << std::endl;
#endif
cli_proto.reset();
serv_proto.reset();
NoisyWire client_to_server("Client -> Server", &time, rand, 8, 16, 32); // last value: 32
NoisyWire server_to_client("Server -> Client", &time, rand, 8, 16, 32); // last value: 32
// start feedback loop
cli_proto.initial_app_send(message);
serv_proto.start();
// message loop
for (int j = 0; j < ITER; ++j)
{
client_to_server.xfer(cli_proto, serv_proto);
server_to_client.xfer(serv_proto, cli_proto);
time += time_step;
}
}
cli_proto.finalize();

View File

@ -1,6 +1,10 @@
#!/usr/bin/env bash
./proto &
./proto &
./proto &
./proto &
./proto >log1 &
./proto >log2 &
./proto >log3 &
./proto >log4 &
wait
for f in log1 log2 log3 log4 ; do
echo "*************" $f
tail -5 $f
done