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

Comment additions.

Catch buffer exceptions and increment BUFFER_ERRORS.
This commit is contained in:
James Yonan 2011-12-13 11:13:27 +00:00
parent 1aa65c259b
commit 1b5fa38adb
7 changed files with 356 additions and 248 deletions

View File

@ -18,6 +18,12 @@
#include <openvpn/applecrypto/cf/cfsec.hpp>
#include <openvpn/applecrypto/cf/error.hpp>
// An SSL Context is essentially a configuration that can be used
// to generate an arbitrary number of actual SSL connections objects.
// AppleSSLContext is an SSL Context implementation that uses the
// Mac/iOS SSL library as a backend.
namespace openvpn {
// Represents an SSL configuration that can be used

View File

@ -22,78 +22,86 @@ namespace openvpn {
void decrypt(BufferAllocated& buf, const PacketID::time_t now)
{
// skip null packets
if (!buf.size())
return;
try {
// skip null packets
if (!buf.size())
return;
// verify the HMAC
if (hmac.defined())
// verify the HMAC
if (hmac.defined())
{
unsigned char local_hmac[HMACContext::MAX_HMAC_SIZE];
const size_t hmac_size = hmac.output_size();
const unsigned char *packet_hmac = buf.read_alloc(hmac_size);
hmac.hmac(local_hmac, hmac_size, buf.c_data(), buf.size());
if (std::memcmp(local_hmac, packet_hmac, hmac_size))
{
buf.reset_size();
if (stats)
stats->error(ProtoStats::HMAC_ERRORS);
return;
}
}
// decrypt packet ID + payload
if (cipher.defined())
{
unsigned char iv_buf[CipherContext::MAX_IV_SIZE];
const size_t iv_size = cipher.iv_size();
// extract IV from head of packet
buf.read(iv_buf, iv_size);
// initialize work buffer
frame->prepare(Frame::DECRYPT_WORK, work);
// decrypt from buf -> work
const size_t decrypt_bytes = cipher.decrypt(iv_buf, work.data(), work.max_size(), buf.c_data(), buf.size());
if (!decrypt_bytes)
{
buf.reset_size();
if (stats)
stats->error(ProtoStats::CRYPTO_ERRORS);
return;
}
work.set_size(decrypt_bytes);
// handle different cipher modes
const int cipher_mode = cipher.cipher_mode();
if (cipher_mode == CipherContext::CIPH_CBC_MODE)
{
if (!verify_packet_id(work, now))
{
buf.reset_size();
if (stats)
stats->error(ProtoStats::REPLAY_ERRORS);
return;
}
}
else
{
throw unsupported_cipher_mode();
}
// return cleartext result in buf
buf.swap(work);
}
else // no encryption
{
if (!verify_packet_id(buf, now))
{
buf.reset_size();
if (stats)
stats->error(ProtoStats::REPLAY_ERRORS);
return;
}
}
}
catch (buffer_exception& e)
{
unsigned char local_hmac[HMACContext::MAX_HMAC_SIZE];
const size_t hmac_size = hmac.output_size();
const unsigned char *packet_hmac = buf.read_alloc(hmac_size);
hmac.hmac(local_hmac, hmac_size, buf.c_data(), buf.size());
if (std::memcmp(local_hmac, packet_hmac, hmac_size))
{
buf.reset_size();
if (stats)
stats->error(ProtoStats::HMAC_ERRORS);
return;
}
}
// decrypt packet ID + payload
if (cipher.defined())
{
unsigned char iv_buf[CipherContext::MAX_IV_SIZE];
const size_t iv_size = cipher.iv_size();
// extract IV from head of packet
buf.read(iv_buf, iv_size);
// initialize work buffer
frame->prepare(Frame::DECRYPT_WORK, work);
// decrypt from buf -> work
const size_t decrypt_bytes = cipher.decrypt(iv_buf, work.data(), work.max_size(), buf.c_data(), buf.size());
if (!decrypt_bytes)
{
buf.reset_size();
if (stats)
stats->error(ProtoStats::CRYPTO_ERRORS);
return;
}
work.set_size(decrypt_bytes);
// handle different cipher modes
const int cipher_mode = cipher.cipher_mode();
if (cipher_mode == CipherContext::CIPH_CBC_MODE)
{
if (!verify_packet_id(work, now))
{
buf.reset_size();
if (stats)
stats->error(ProtoStats::REPLAY_ERRORS);
return;
}
}
else
{
throw unsupported_cipher_mode();
}
// return cleartext result in buf
buf.swap(work);
}
else // no encryption
{
if (!verify_packet_id(buf, now))
{
buf.reset_size();
if (stats)
stats->error(ProtoStats::REPLAY_ERRORS);
return;
}
buf.reset_size();
if (stats)
stats->error(ProtoStats::BUFFER_ERRORS);
}
}

View File

@ -18,6 +18,12 @@
#include <openvpn/openssl/pki/x509store.hpp>
#include <openvpn/openssl/bio/bio_memq_stream.hpp>
// An SSL Context is essentially a configuration that can be used
// to generate an arbitrary number of actual SSL connections objects.
// OpenSSLContext is an SSL Context implementation that uses the
// OpenSSL library as a backend.
namespace openvpn {
// Represents an SSL configuration that can be used

View File

@ -140,22 +140,23 @@ namespace openvpn {
int pid_time_backtrack;
int pid_debug_level; // PacketIDReceive::DEBUG_x levels
// timeout parameters
Time::Duration handshake_window;
Time::Duration become_primary;
Time::Duration renegotiate;
Time::Duration expire;
// timeout parameters, relative to construction of KeyContext object
Time::Duration handshake_window; // SSL/TLS negotiation must complete by this time
Time::Duration become_primary; // KeyContext (that is ACTIVE) becomes primary at this time
Time::Duration renegotiate; // start SSL/TLS renegotiation at this time
Time::Duration expire; // KeyContext expires at this time
};
// Used to describe an incoming network packet
class PacketType
{
friend class ProtoContext;
enum {
DEFINED=1<<0,
CONTROL=1<<1,
SECONDARY=1<<2,
SOFT_RESET=1<<3,
DEFINED=1<<0, // packet is valid (otherwise invalid)
CONTROL=1<<1, // packet for control channel (otherwise for data channel)
SECONDARY=1<<2, // packet is associated with secondary KeyContext (otherwise primary)
SOFT_RESET=1<<3, // packet is a CONTROL_SOFT_RESET_V1 message indicating a request for SSL/TLS renegotiation
};
public:
@ -255,6 +256,9 @@ namespace openvpn {
}
protected:
// Packet structure for managing network packets, as passed as a template
// parameter to ProtoStackBase
class Packet
{
friend class ProtoContext;
@ -355,6 +359,7 @@ namespace openvpn {
next_event_time = construct_time + p.config->handshake_window;
}
// need to call only on the initiator side of the connection (i.e. client)
void start()
{
if (state == C_INITIAL)
@ -365,6 +370,7 @@ namespace openvpn {
}
}
// control channel flush
void flush()
{
if (dirty)
@ -376,12 +382,14 @@ namespace openvpn {
}
}
// retransmit packets as part of reliability layer
void retransmit()
{
// note that we don't set dirty here
Base::retransmit();
}
// when should we next call retransmit function
Time next_retransmit() const
{
const Time t = Base::next_retransmit();
@ -391,6 +399,7 @@ namespace openvpn {
return next_event_time;
}
// send app-level cleartext data to peer via SSL
void app_send(BufferPtr& bp)
{
if (state >= ACTIVE)
@ -402,12 +411,14 @@ namespace openvpn {
app_pre_write_queue.push_back(bp);
}
// pass received ciphertext packets on network to SSL/reliability layers
void net_recv(Packet& pkt)
{
Base::net_recv(pkt);
dirty = true;
}
// data channel encrypt
void encrypt(BufferAllocated& buf)
{
if (state >= ACTIVE && !invalidated())
@ -420,6 +431,7 @@ namespace openvpn {
buf.reset_size(); // no crypto context available
}
// data channel decrypt
void decrypt(BufferAllocated& buf)
{
if (state >= ACTIVE && !invalidated())
@ -431,6 +443,8 @@ namespace openvpn {
buf.reset_size(); // no crypto context available
}
// usually called by parent ProtoContext object when this KeyContext
// has been retired.
void prepare_expire()
{
current_event = KEV_NONE;
@ -438,6 +452,7 @@ namespace openvpn {
next_event_time = construct_time + proto.config->expire;
}
// is an KEV_x event pending?
bool event_pending()
{
if (current_event == KEV_NONE && *now >= next_event_time)
@ -445,92 +460,110 @@ namespace openvpn {
return current_event != KEV_NONE;
}
// get KEV_x event
EventType get_event() const { return current_event; }
// clear KEV_x event
void reset_event() { current_event = KEV_NONE; }
// Was session invalidated by an exception?
// was session invalidated by an exception?
bool invalidated() const { return Base::invalidated(); }
// our Key ID in the OpenVPN protocol
unsigned int key_id() const { return key_id_; }
// indicates that data channel is keyed and ready to encrypt/decrypt packets
bool data_channel_ready() const { return state >= ACTIVE; }
bool is_dirty() const { return dirty; }
// time that our state transitioned to ACTIVE
Time reached_active() const { return reached_active_time_; }
// validate the integrity of a packet
static bool validate(const Buffer& net_buf, ProtoContext& proto, TimePtr now)
{
Buffer recv(net_buf);
if (proto.use_tls_auth)
{
const unsigned char *orig_data = recv.data();
const size_t orig_size = recv.size();
// advance buffer past initial op byte
recv.advance(1);
// get source PSID
ProtoSessionID src_psid(recv);
// verify HMAC
try {
Buffer recv(net_buf);
if (proto.use_tls_auth)
{
const unsigned char *hmac = recv.read_alloc(proto.hmac_size);
if (!proto.ta_hmac_recv.hmac2_cmp(hmac, proto.hmac_size, orig_data, orig_size))
const unsigned char *orig_data = recv.data();
const size_t orig_size = recv.size();
// advance buffer past initial op byte
recv.advance(1);
// get source PSID
ProtoSessionID src_psid(recv);
// verify HMAC
{
const unsigned char *hmac = recv.read_alloc(proto.hmac_size);
if (!proto.ta_hmac_recv.hmac2_cmp(hmac, proto.hmac_size, orig_data, orig_size))
return false;
}
// verify source PSID
if (!proto.psid_peer.match(src_psid))
return false;
// read tls_auth packet ID
const PacketID pid = proto.ta_pid_recv.read_next(recv);
// get current time_t
const PacketID::time_t t = now->seconds_since_epoch();
// verify tls_auth packet ID
const bool pid_ok = proto.ta_pid_recv.test(pid, t);
// make sure that our own PSID is contained in packet received from peer
if (ReliableAck::ack_skip(recv))
{
ProtoSessionID dest_psid(recv);
if (!proto.psid_self.match(dest_psid))
return false;
}
return pid_ok;
}
else
{
// advance buffer past initial op byte
recv.advance(1);
// verify source PSID
if (!proto.psid_peer.match(src_psid))
return false;
// verify source PSID
ProtoSessionID src_psid(recv);
if (!proto.psid_peer.match(src_psid))
return false;
// read tls_auth packet ID
const PacketID pid = proto.ta_pid_recv.read_next(recv);
// make sure that our own PSID is contained in packet received from peer
if (ReliableAck::ack_skip(recv))
{
ProtoSessionID dest_psid(recv);
if (!proto.psid_self.match(dest_psid))
return false;
}
// get current time_t
const PacketID::time_t t = now->seconds_since_epoch();
// verify tls_auth packet ID
const bool pid_ok = proto.ta_pid_recv.test(pid, t);
// make sure that our own PSID is contained in packet received from peer
if (ReliableAck::ack_skip(recv))
{
ProtoSessionID dest_psid(recv);
if (!proto.psid_self.match(dest_psid))
return false;
}
return pid_ok;
}
else
return true;
}
}
catch (buffer_exception& e)
{
// advance buffer past initial op byte
recv.advance(1);
// verify source PSID
ProtoSessionID src_psid(recv);
if (!proto.psid_peer.match(src_psid))
return false;
// make sure that our own PSID is contained in packet received from peer
if (ReliableAck::ack_skip(recv))
{
ProtoSessionID dest_psid(recv);
if (!proto.psid_self.match(dest_psid))
return false;
}
return true;
return false;
}
}
private:
// called by ProtoStackBase when session is invalidated
virtual void invalidate_callback()
{
reached_active_time_ = Time();
}
// Trigger a new SSL/TLS negotiation if packet ID (a 32-bit unsigned int)
// is getting close to wrapping around. If it wraps back to 0 without
// a renegotiation, it would cause the relay protection logic to wrongly
// think that all packets are replays.
void test_pid_wrap()
{
if (!handled_pid_wrap && crypto.encrypt.pid_send.wrap_warning())
@ -710,6 +743,8 @@ namespace openvpn {
active_event();
}
// use the TLS PRF construction to exchange session keys for building
// the data channel crypto context
void generate_session_keys()
{
OpenVPNStaticKey key;
@ -720,6 +755,8 @@ namespace openvpn {
tlsprf_peer.erase();
}
// given our ephemeral session key, initialize the components of the
// OpenVPN data channel protocol
void init_data_channel_crypto_context(const OpenVPNStaticKey& key)
{
const Config& c = *proto.config;
@ -842,116 +879,122 @@ namespace openvpn {
virtual bool decapsulate(Packet& pkt)
{
Buffer& recv = *pkt.buf;
try {
Buffer& recv = *pkt.buf;
if (proto.use_tls_auth)
{
const unsigned char *orig_data = recv.data();
const size_t orig_size = recv.size();
// advance buffer past initial op byte
recv.advance(1);
// get source PSID
ProtoSessionID src_psid(recv);
// verify HMAC
if (proto.use_tls_auth)
{
const unsigned char *hmac = recv.read_alloc(proto.hmac_size);
if (!proto.ta_hmac_recv.hmac2_cmp(hmac, proto.hmac_size, orig_data, orig_size))
const unsigned char *orig_data = recv.data();
const size_t orig_size = recv.size();
// advance buffer past initial op byte
recv.advance(1);
// get source PSID
ProtoSessionID src_psid(recv);
// verify HMAC
{
const unsigned char *hmac = recv.read_alloc(proto.hmac_size);
if (!proto.ta_hmac_recv.hmac2_cmp(hmac, proto.hmac_size, orig_data, orig_size))
{
proto.stats->error(ProtoStats::HMAC_ERRORS);
return false;
}
}
// verify source PSID
if (!verify_src_psid(src_psid))
return false;
// read tls_auth packet ID
const PacketID pid = proto.ta_pid_recv.read_next(recv);
// get current time_t
const PacketID::time_t t = now->seconds_since_epoch();
// verify tls_auth packet ID
const bool pid_ok = proto.ta_pid_recv.test(pid, t);
// process ACKs sent by peer (if packet ID check failed,
// read the ACK IDs, but don't modify the rel_send object).
if (ReliableAck::ack(rel_send, recv, pid_ok))
{
proto.stats->error(ProtoStats::HMAC_ERRORS);
return false;
}
// make sure that our own PSID is contained in packet received from peer
if (!verify_dest_psid(recv))
return false;
}
// for CONTROL packets only, not ACK
if (pkt.opcode != ACK_V1)
{
// get message sequence number
const id_t id = ReliableAck::read_id(recv);
if (pid_ok)
{
// try to push message into reliable receive object
const unsigned int rflags = rel_recv.receive(pkt, id);
// should we ACK packet back to sender?
if (rflags & ReliableRecv::ACK_TO_SENDER)
xmit_acks.push_back(id); // ACK packet to sender
// was packet accepted by reliable receive object?
if (rflags & ReliableRecv::IN_WINDOW)
{
proto.ta_pid_recv.add(pid, t); // remember tls_auth packet ID so that it can't be replayed
return true;
}
}
else // treat as replay
{
proto.stats->error(ProtoStats::REPLAY_ERRORS);
if (pid.is_valid())
xmit_acks.push_back(id); // even replayed packets must be ACKed or protocol could deadlock
}
}
}
else // non tls_auth mode
{
// advance buffer past initial op byte
recv.advance(1);
// verify source PSID
if (!verify_src_psid(src_psid))
return false;
// verify source PSID
ProtoSessionID src_psid(recv);
if (!verify_src_psid(src_psid))
return false;
// read tls_auth packet ID
const PacketID pid = proto.ta_pid_recv.read_next(recv);
// process ACKs sent by peer
if (ReliableAck::ack(rel_send, recv, true))
{
// make sure that our own PSID is in packet received from peer
if (!verify_dest_psid(recv))
return false;
}
// get current time_t
const PacketID::time_t t = now->seconds_since_epoch();
// for CONTROL packets only, not ACK
if (pkt.opcode != ACK_V1)
{
// get message sequence number
const id_t id = ReliableAck::read_id(recv);
// verify tls_auth packet ID
const bool pid_ok = proto.ta_pid_recv.test(pid, t);
// try to push message into reliable receive object
const unsigned int rflags = rel_recv.receive(pkt, id);
// process ACKs sent by peer (if packet ID check failed,
// read the ACK IDs, but don't modify the rel_send object).
if (ReliableAck::ack(rel_send, recv, pid_ok))
{
// make sure that our own PSID is contained in packet received from peer
if (!verify_dest_psid(recv))
return false;
}
// should we ACK packet back to sender?
if (rflags & ReliableRecv::ACK_TO_SENDER)
xmit_acks.push_back(id); // ACK packet to sender
// for CONTROL packets only, not ACK
if (pkt.opcode != ACK_V1)
{
// get message sequence number
const id_t id = ReliableAck::read_id(recv);
if (pid_ok)
{
// try to push message into reliable receive object
const unsigned int rflags = rel_recv.receive(pkt, id);
// should we ACK packet back to sender?
if (rflags & ReliableRecv::ACK_TO_SENDER)
xmit_acks.push_back(id); // ACK packet to sender
// was packet accepted by reliable receive object?
if (rflags & ReliableRecv::IN_WINDOW)
{
proto.ta_pid_recv.add(pid, t); // remember tls_auth packet ID so that it can't be replayed
return true;
}
}
else // treat as replay
{
proto.stats->error(ProtoStats::REPLAY_ERRORS);
if (pid.is_valid())
xmit_acks.push_back(id); // even replayed packets must be ACKed or protocol could deadlock
}
}
}
else // non tls_auth mode
// was packet accepted by reliable receive object?
if (rflags & ReliableRecv::IN_WINDOW)
return true;
}
}
}
catch (buffer_exception& e)
{
// advance buffer past initial op byte
recv.advance(1);
// verify source PSID
ProtoSessionID src_psid(recv);
if (!verify_src_psid(src_psid))
return false;
// process ACKs sent by peer
if (ReliableAck::ack(rel_send, recv, true))
{
// make sure that our own PSID is in packet received from peer
if (!verify_dest_psid(recv))
return false;
}
// for CONTROL packets only, not ACK
if (pkt.opcode != ACK_V1)
{
// get message sequence number
const id_t id = ReliableAck::read_id(recv);
// try to push message into reliable receive object
const unsigned int rflags = rel_recv.receive(pkt, id);
// should we ACK packet back to sender?
if (rflags & ReliableRecv::ACK_TO_SENDER)
xmit_acks.push_back(id); // ACK packet to sender
// was packet accepted by reliable receive object?
if (rflags & ReliableRecv::IN_WINDOW)
return true;
}
proto.stats->error(ProtoStats::BUFFER_ERRORS);
}
return false;
}
@ -988,8 +1031,8 @@ namespace openvpn {
public:
OPENVPN_SIMPLE_EXCEPTION(select_key_context_error);
ProtoContext(const typename Config::Ptr& config_arg,
const ProtoStats::Ptr& stats_arg)
ProtoContext(const typename Config::Ptr& config_arg, // configuration
const ProtoStats::Ptr& stats_arg) // error stats
: config(config_arg),
stats(stats_arg),
hmac_size(0),
@ -1035,6 +1078,7 @@ namespace openvpn {
virtual ~ProtoContext() {}
// return the PacketType of an incoming network packet
PacketType packet_type(const Buffer& buf)
{
PacketType pt;
@ -1058,11 +1102,13 @@ namespace openvpn {
return pt;
}
// start protocol negotiation
void start()
{
primary->start();
}
// trigger a protocol renegotiation
void renegotiate()
{
// initialize secondary key context
@ -1070,7 +1116,8 @@ namespace openvpn {
secondary->start();
}
// Should be called at the end of sequence of operations.
// Should be called at the end of sequence of send/recv
// operations on underlying protocol object.
// If control_channel is true, do a full flush.
// If control_channel is false, optimize flush for data
// channel only.
@ -1084,6 +1131,7 @@ namespace openvpn {
}
}
// retransmit unacknowleged packets as part of the reliability layer
void retransmit()
{
// primary
@ -1094,6 +1142,7 @@ namespace openvpn {
secondary->retransmit();
}
// when should we next call retransmit?
Time next_retransmit() const
{
const Time p = primary->next_retransmit();
@ -1106,6 +1155,8 @@ namespace openvpn {
return p;
}
// send app-level cleartext to remote peer
void control_send(BufferPtr& app_bp)
{
select_control_send_context().app_send(app_bp);
@ -1118,11 +1169,14 @@ namespace openvpn {
select_control_send_context().app_send(bp);
}
// validate a control channel network packet
bool control_net_validate(const PacketType& type, const Buffer& net_buf)
{
return type.is_defined() && KeyContext::validate(net_buf, *this, config->now);
}
// pass received control channel network packets (ciphertext) into protocol object
void control_net_recv(const PacketType& type, BufferAllocated& net_buf)
{
BufferPtr bp = new BufferAllocated();
@ -1141,11 +1195,14 @@ namespace openvpn {
select_key_context(type, true).net_recv(pkt);
}
// encrypt a data channel packet using primary KeyContext
void data_encrypt(BufferAllocated& in_out)
{
primary->encrypt(in_out);
}
// decrypt a data channel packet (automatically select primary
// or secondary KeyContext based on packet content)
void data_decrypt(const PacketType& type, BufferAllocated& in_out)
{
select_key_context(type, false).decrypt(in_out);
@ -1160,7 +1217,7 @@ namespace openvpn {
// can we call data_encrypt or data_decrypt yet?
bool data_channel_ready() const { return primary->data_channel_ready(); }
// total number of SSL/TLS negotiations
// total number of SSL/TLS negotiations during lifetime of ProtoContext object
unsigned int negotiations() const { return n_key_ids; }
// total number of SSL/TLS negotiations that failed to complete during handshake window
@ -1196,6 +1253,7 @@ namespace openvpn {
return false;
}
// select a KeyContext (primary or secondary) for received network packets
KeyContext& select_key_context(const PacketType& type, const bool control)
{
const unsigned int flags = type.flags & (PacketType::DEFINED|PacketType::SECONDARY|PacketType::CONTROL);
@ -1216,15 +1274,13 @@ namespace openvpn {
throw select_key_context_error();
}
// Select a KeyContext (primary or secondar) for control channel sends.
// If only primary exists, choose primary.
// If both primary and secondary exists, choose the one that reached
// the active state most recently.
// Select a KeyContext (primary or secondary) for control channel sends.
KeyContext& select_control_send_context()
{
return *primary;
}
// Process KEV_x events
bool process_events()
{
bool did_work;
@ -1250,6 +1306,9 @@ namespace openvpn {
return count > 1;
}
// Promote a newly renegotiated KeyContext to primary status.
// This is usually triggered by become_primary variable (Time::Duration)
// in Config.
void promote_secondary_to_primary()
{
if (!secondary->invalidated())

View File

@ -14,6 +14,14 @@
#include <openvpn/reliable/relack.hpp>
#include <openvpn/frame/frame.hpp>
// ProtoStackBase is designed to allow general-purpose protocols (including
// but not limited to OpenVPN) to run over SSL, where the underlying transport
// layer is unreliable, such as UDP. The OpenVPN protocol implementation in
// proto.hpp (ProtoContext) layers on top of ProtoStackBase.
// ProtoStackBase is independent of any particular SSL implementation, and
// accepts the SSL object type as a template parameter. See OpenSSLContext
// and AppleSSLContext for existing implementations.
namespace openvpn {
// PACKET type must define the following methods:
@ -50,12 +58,12 @@ namespace openvpn {
OPENVPN_SIMPLE_EXCEPTION(proto_stack_invalidated);
ProtoStackBase(SSLContext& ctx,
TimePtr now_arg,
const Frame::Ptr& frame,
const ProtoStats::Ptr& stats_arg,
const id_t span,
const size_t max_ack_list)
ProtoStackBase(SSLContext& ctx, // SSL context object that can be used to generate new SSL sessions
TimePtr now_arg, // pointer to current time
const Frame::Ptr& frame, // contains info on how to allocate and align buffers
const ProtoStats::Ptr& stats_arg, // error statistics
const id_t span, // basically the window size for our reliability layer
const size_t max_ack_list) // maximum number of ACK messages to bundle in one packet
: ssl_(ctx.ssl()),
frame_(frame),
up_stack_reentry_level(0),
@ -217,7 +225,7 @@ namespace openvpn {
// a ready-to-use state.
virtual void raw_recv(PACKET& raw_pkt) = 0;
// called if session is invalidated by an error
// called if session is invalidated by an error (optional)
virtual void invalidate_callback() {}
// END of VIRTUAL METHODS

View File

@ -6,13 +6,17 @@
#include <cstring>
#include <limits>
// Unit test for OpenVPN protocol
#define PACKET_ID_EXTRA_LOG_INFO
#define USE_TLS_AUTH
// number of threads to use for test
#ifndef N_THREADS
#define N_THREADS 1
#endif
// number of iterations
#ifndef ITER
#define ITER 1000000
#endif
@ -77,6 +81,9 @@ const char message[] =
#endif
;
// A "Drought" measures the maximum period of time between
// any two successive events. Used to measure worst-case
// packet loss.
class DroughtMeasure
{
public:
@ -104,6 +111,7 @@ private:
Time::Duration drought;
};
// test the OpenVPN protocol implementation in ProtoContext
template <typename SSL_CONTEXT>
class TestProto : public ProtoContext<SSL_CONTEXT>
{
@ -253,6 +261,8 @@ private:
char progress_[11];
};
// Simulate a noisy transmission channel where packets can be dropped,
// reordered, or corrupted.
class NoisyWire
{
public:
@ -404,6 +414,7 @@ private:
std::deque<BufferPtr> wire;
};
// execute the unit test in one thread
void test(const int thread_num)
{
try {

10
test/ssl/proto.txt Normal file
View File

@ -0,0 +1,10 @@
GCC_EXTRA="-DN_THREADS=4 -DITER=100000000" build proto
*** app bytes=12677543355 net_bytes=7425359992 data_bytes=12931806340 prog=0037843411/0037843410 D=16900/800/16800/900 N=146564/146564 F=0/0
*** app bytes=12683953245 net_bytes=7427850971 data_bytes=12931599996 prog=0037862545/0037862544 D=16800/900/14700/900 N=146496/146496 F=0/0
*** app bytes=12680471255 net_bytes=7425724143 data_bytes=12931798152 prog=0037852151/0037852150 D=16800/900/16800/800 N=146419/146419 F=0/0
*** app bytes=12681776415 net_bytes=7426684552 data_bytes=12931802571 prog=0037856047/0037856046 D=16800/900/16800/800 N=146614/146614 F=0/0
real 53m0.004s
user 204m53.656s
sys 2m28.965s