2014-07-21 05:22:06 +02:00
|
|
|
// OpenVPN -- An application to securely tunnel IP networks
|
|
|
|
// over a single port, with support for SSL/TLS-based
|
|
|
|
// session authentication and key exchange,
|
|
|
|
// packet encryption, packet authentication, and
|
|
|
|
// packet compression.
|
2012-08-24 23:13:42 +02:00
|
|
|
//
|
2015-01-06 20:56:21 +01:00
|
|
|
// Copyright (C) 2012-2015 OpenVPN Technologies, Inc.
|
2012-08-24 23:13:42 +02:00
|
|
|
//
|
2014-07-21 05:22:06 +02:00
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License Version 3
|
|
|
|
// as published by the Free Software Foundation.
|
2012-08-24 23:13:42 +02:00
|
|
|
//
|
2014-07-21 05:22:06 +02:00
|
|
|
// 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 Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program in the COPYING file.
|
|
|
|
// If not, see <http://www.gnu.org/licenses/>.
|
2012-08-24 23:13:42 +02:00
|
|
|
|
2012-11-23 07:18:43 +01:00
|
|
|
// Manage OpenVPN protocol Packet IDs for packet replay detection
|
|
|
|
|
2011-09-29 01:12:37 +02:00
|
|
|
#ifndef OPENVPN_CRYPTO_PACKET_ID_H
|
|
|
|
#define OPENVPN_CRYPTO_PACKET_ID_H
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <sstream>
|
2015-04-24 01:55:07 +02:00
|
|
|
#include <cstdint> // for std::uint32_t
|
2011-09-29 01:12:37 +02:00
|
|
|
|
|
|
|
#include <boost/asio.hpp>
|
|
|
|
|
2011-09-29 08:35:23 +02:00
|
|
|
#include <openvpn/common/types.hpp>
|
2011-09-29 01:12:37 +02:00
|
|
|
#include <openvpn/common/exception.hpp>
|
|
|
|
#include <openvpn/common/circ_list.hpp>
|
2012-01-11 07:15:32 +01:00
|
|
|
#include <openvpn/common/socktypes.hpp>
|
2014-12-21 18:32:37 +01:00
|
|
|
#include <openvpn/common/likely.hpp>
|
2011-11-09 06:52:52 +01:00
|
|
|
#include <openvpn/time/time.hpp>
|
2011-10-06 02:18:46 +02:00
|
|
|
#include <openvpn/buffer/buffer.hpp>
|
2013-05-22 07:13:11 +02:00
|
|
|
#include <openvpn/log/sessionstats.hpp>
|
2011-09-29 01:12:37 +02:00
|
|
|
|
|
|
|
namespace openvpn {
|
2011-10-06 02:18:46 +02:00
|
|
|
/*
|
|
|
|
* Communicate packet-id over the wire.
|
|
|
|
* A short packet-id is just a 32 bit
|
|
|
|
* sequence number. A long packet-id
|
|
|
|
* includes a timestamp as well.
|
|
|
|
*
|
|
|
|
* Long packet-ids are used as IVs for
|
|
|
|
* CFB/OFB ciphers.
|
|
|
|
*
|
|
|
|
* This data structure is always sent
|
|
|
|
* over the net in network byte order,
|
|
|
|
* by calling htonpid, ntohpid,
|
|
|
|
* htontime, and ntohtime on the
|
|
|
|
* data elements to change them
|
|
|
|
* to and from standard sizes.
|
|
|
|
*
|
|
|
|
* In addition, time is converted to
|
|
|
|
* a PacketID::net_time_t before sending,
|
|
|
|
* since openvpn always
|
|
|
|
* uses a 32-bit time_t but some
|
|
|
|
* 64 bit platforms use a
|
|
|
|
* 64 bit time_t.
|
|
|
|
*/
|
|
|
|
struct PacketID
|
|
|
|
{
|
2015-04-24 01:55:07 +02:00
|
|
|
typedef std::uint32_t id_t;
|
|
|
|
typedef std::uint32_t net_time_t;
|
2011-11-01 14:00:49 +01:00
|
|
|
typedef Time::base_type time_t;
|
2011-10-06 02:18:46 +02:00
|
|
|
|
|
|
|
enum {
|
2011-12-02 23:00:56 +01:00
|
|
|
SHORT_FORM = 0, // short form of ID (4 bytes)
|
|
|
|
LONG_FORM = 1, // long form of ID (8 bytes)
|
|
|
|
|
2011-12-05 07:11:51 +01:00
|
|
|
UNDEF = 0, // special undefined/null id_t value
|
2011-10-06 02:18:46 +02:00
|
|
|
};
|
2011-09-29 01:12:37 +02:00
|
|
|
|
2011-12-02 23:00:56 +01:00
|
|
|
id_t id; // legal values are 1 through 2^32-1
|
|
|
|
time_t time; // converted to PacketID::net_time_t before transmission
|
2011-09-29 01:12:37 +02:00
|
|
|
|
2011-12-14 12:34:33 +01:00
|
|
|
static size_t size(const int form)
|
|
|
|
{
|
|
|
|
if (form == PacketID::LONG_FORM)
|
2011-12-16 11:02:15 +01:00
|
|
|
return sizeof(id_t) + sizeof(net_time_t);
|
2011-12-14 12:34:33 +01:00
|
|
|
else
|
|
|
|
return sizeof(id_t);
|
|
|
|
}
|
|
|
|
|
2011-12-05 07:11:51 +01:00
|
|
|
bool is_valid() const
|
|
|
|
{
|
2011-12-11 09:28:55 +01:00
|
|
|
return id != UNDEF;
|
2011-12-05 07:11:51 +01:00
|
|
|
}
|
|
|
|
|
2011-10-06 02:18:46 +02:00
|
|
|
void reset()
|
|
|
|
{
|
|
|
|
id = id_t(0);
|
|
|
|
time = time_t(0);
|
|
|
|
}
|
2011-09-29 01:12:37 +02:00
|
|
|
|
2011-10-06 02:18:46 +02:00
|
|
|
void read(Buffer& buf, const int form)
|
2011-09-29 01:12:37 +02:00
|
|
|
{
|
2011-10-06 02:18:46 +02:00
|
|
|
id_t net_id;
|
|
|
|
net_time_t net_time;
|
|
|
|
|
2011-10-25 19:32:26 +02:00
|
|
|
buf.read ((unsigned char *)&net_id, sizeof (net_id));
|
2011-10-06 02:18:46 +02:00
|
|
|
id = ntohl (net_id);
|
|
|
|
|
|
|
|
if (form == LONG_FORM)
|
|
|
|
{
|
2011-10-25 19:32:26 +02:00
|
|
|
buf.read ((unsigned char *)&net_time, sizeof (net_time));
|
2011-10-06 02:18:46 +02:00
|
|
|
time = ntohl (net_time);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
time = time_t(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void write(Buffer& buf, const int form, const bool prepend) const
|
|
|
|
{
|
|
|
|
const id_t net_id = htonl(id);
|
|
|
|
const net_time_t net_time = htonl(time);
|
|
|
|
|
|
|
|
if (prepend)
|
|
|
|
{
|
|
|
|
if (form == LONG_FORM)
|
2011-10-25 19:32:26 +02:00
|
|
|
buf.prepend ((unsigned char *)&net_time, sizeof (net_time));
|
|
|
|
buf.prepend ((unsigned char *)&net_id, sizeof (net_id));
|
2011-10-06 02:18:46 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-10-25 19:32:26 +02:00
|
|
|
buf.write ((unsigned char *)&net_id, sizeof (net_id));
|
2011-10-06 02:18:46 +02:00
|
|
|
if (form == LONG_FORM)
|
2011-10-25 19:32:26 +02:00
|
|
|
buf.write ((unsigned char *)&net_time, sizeof (net_time));
|
2011-10-06 02:18:46 +02:00
|
|
|
}
|
|
|
|
}
|
2011-09-29 01:12:37 +02:00
|
|
|
|
2013-05-22 07:13:11 +02:00
|
|
|
#ifdef OPENVPN_INSTRUMENTATION
|
2011-10-06 02:18:46 +02:00
|
|
|
std::string str() const
|
|
|
|
{
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "[" << time << "," << id << "]";
|
|
|
|
return os.str();
|
|
|
|
}
|
2013-05-22 07:13:11 +02:00
|
|
|
#endif
|
2011-10-06 02:18:46 +02:00
|
|
|
};
|
2011-09-29 01:12:37 +02:00
|
|
|
|
2011-10-06 02:18:46 +02:00
|
|
|
struct PacketIDConstruct : public PacketID
|
|
|
|
{
|
|
|
|
PacketIDConstruct(const PacketID::time_t v_time = PacketID::time_t(0), const PacketID::id_t v_id = PacketID::id_t(0))
|
2011-09-29 01:12:37 +02:00
|
|
|
{
|
2011-10-06 02:18:46 +02:00
|
|
|
id = v_id;
|
|
|
|
time = v_time;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class PacketIDSend
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
OPENVPN_SIMPLE_EXCEPTION(packet_id_wrap);
|
|
|
|
|
|
|
|
PacketIDSend()
|
|
|
|
{
|
|
|
|
init(PacketID::SHORT_FORM);
|
|
|
|
}
|
|
|
|
|
|
|
|
void init(const int form) // PacketID::LONG_FORM or PacketID::SHORT_FORM
|
|
|
|
{
|
|
|
|
pid_.id = PacketID::id_t(0);
|
|
|
|
pid_.time = PacketID::time_t(0);
|
|
|
|
form_ = form;
|
|
|
|
}
|
2011-09-29 01:12:37 +02:00
|
|
|
|
2011-11-09 06:52:52 +01:00
|
|
|
PacketID next(const PacketID::time_t now)
|
2011-10-04 07:34:04 +02:00
|
|
|
{
|
2011-10-06 02:18:46 +02:00
|
|
|
PacketID ret;
|
|
|
|
if (!pid_.time)
|
|
|
|
pid_.time = now;
|
|
|
|
ret.id = ++pid_.id;
|
2014-12-21 18:32:37 +01:00
|
|
|
if (unlikely(!pid_.id)) // wraparound
|
2011-10-06 02:18:46 +02:00
|
|
|
{
|
|
|
|
if (form_ != PacketID::LONG_FORM)
|
|
|
|
throw packet_id_wrap();
|
2011-10-04 07:34:04 +02:00
|
|
|
pid_.time = now;
|
2011-10-06 02:18:46 +02:00
|
|
|
ret.id = pid_.id = 1;
|
|
|
|
}
|
|
|
|
ret.time = pid_.time;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-11-09 06:52:52 +01:00
|
|
|
void write_next(Buffer& buf, const bool prepend, const PacketID::time_t now)
|
2011-10-06 02:18:46 +02:00
|
|
|
{
|
2011-11-09 06:52:52 +01:00
|
|
|
const PacketID pid = next(now);
|
2011-10-06 02:18:46 +02:00
|
|
|
pid.write(buf, form_, prepend);
|
|
|
|
}
|
2011-10-04 07:34:04 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* In TLS mode, when a packet ID gets to this level,
|
|
|
|
* start thinking about triggering a new
|
|
|
|
* SSL/TLS handshake.
|
|
|
|
*/
|
2011-12-13 05:46:56 +01:00
|
|
|
bool wrap_warning() const
|
2011-10-06 02:18:46 +02:00
|
|
|
{
|
|
|
|
const PacketID::id_t wrap_at = 0xFF000000;
|
|
|
|
return pid_.id >= wrap_at;
|
|
|
|
}
|
2011-10-04 07:34:04 +02:00
|
|
|
|
2013-05-22 07:13:11 +02:00
|
|
|
#ifdef OPENVPN_INSTRUMENTATION
|
2011-10-06 02:18:46 +02:00
|
|
|
std::string str() const
|
|
|
|
{
|
|
|
|
std::string ret;
|
|
|
|
ret = pid_.str();
|
|
|
|
if (form_ == PacketID::LONG_FORM)
|
|
|
|
ret += 'L';
|
|
|
|
return ret;
|
|
|
|
}
|
2011-10-04 07:34:04 +02:00
|
|
|
#endif
|
|
|
|
|
2011-10-06 02:18:46 +02:00
|
|
|
private:
|
|
|
|
PacketID pid_;
|
|
|
|
int form_;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is the data structure we keep on the receiving side,
|
|
|
|
* to check that no packet-id (i.e. sequence number + optional timestamp)
|
|
|
|
* is accepted more than once.
|
|
|
|
*/
|
|
|
|
class PacketIDReceive
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
OPENVPN_SIMPLE_EXCEPTION(packet_id_backtrack_out_of_range);
|
|
|
|
OPENVPN_SIMPLE_EXCEPTION(packet_id_not_initialized);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Maximum allowed backtrack in
|
|
|
|
* sequence number due to packets arriving
|
|
|
|
* out of order.
|
|
|
|
*/
|
|
|
|
enum {
|
|
|
|
MIN_SEQ_BACKTRACK = 0,
|
|
|
|
MAX_SEQ_BACKTRACK = 65536,
|
|
|
|
DEFAULT_SEQ_BACKTRACK = 64
|
2011-10-04 07:34:04 +02:00
|
|
|
};
|
|
|
|
|
2011-09-29 01:12:37 +02:00
|
|
|
/*
|
2011-10-06 02:18:46 +02:00
|
|
|
* Maximum allowed backtrack in
|
|
|
|
* seconds due to packets arriving
|
|
|
|
* out of order.
|
2011-09-29 01:12:37 +02:00
|
|
|
*/
|
2011-10-06 02:18:46 +02:00
|
|
|
enum {
|
|
|
|
MIN_TIME_BACKTRACK = 0,
|
|
|
|
MAX_TIME_BACKTRACK = 600,
|
|
|
|
DEFAULT_TIME_BACKTRACK = 15
|
|
|
|
};
|
2011-09-29 01:12:37 +02:00
|
|
|
|
2011-10-06 02:18:46 +02:00
|
|
|
/*
|
|
|
|
* Special PacketID::time_t value that indicates that
|
|
|
|
* sequence number has expired.
|
|
|
|
*/
|
|
|
|
enum {
|
|
|
|
SEQ_UNSEEN = 0,
|
|
|
|
SEQ_EXPIRED = 1
|
|
|
|
};
|
2011-09-29 01:12:37 +02:00
|
|
|
|
2011-10-06 02:18:46 +02:00
|
|
|
/*
|
|
|
|
* Do a reap pass through the sequence number
|
|
|
|
* array once every n seconds in order to
|
|
|
|
* expire sequence numbers which can no longer
|
|
|
|
* be accepted because they would violate
|
|
|
|
* TIME_BACKTRACK.
|
|
|
|
*/
|
|
|
|
enum {
|
|
|
|
SEQ_REAP_PERIOD = 5
|
|
|
|
};
|
2011-09-29 01:12:37 +02:00
|
|
|
|
2011-10-06 02:18:46 +02:00
|
|
|
/* mode */
|
|
|
|
enum {
|
|
|
|
UDP_MODE = 0,
|
|
|
|
TCP_MODE = 1
|
|
|
|
};
|
|
|
|
|
|
|
|
PacketIDReceive() : initialized_(false) {}
|
|
|
|
|
|
|
|
bool initialized() const { return initialized_; }
|
|
|
|
|
|
|
|
void init(const int mode, const int form,
|
|
|
|
const int seq_backtrack, const int time_backtrack,
|
2013-05-22 07:13:11 +02:00
|
|
|
const char *name, const int unit,
|
|
|
|
const SessionStats::Ptr& stats_arg)
|
2011-10-06 02:18:46 +02:00
|
|
|
{
|
|
|
|
initialized_ = false;
|
|
|
|
form_ = form;
|
|
|
|
id_ = 0;
|
|
|
|
time_ = 0;
|
|
|
|
last_reap_ = 0;
|
|
|
|
seq_backtrack_ = 0;
|
|
|
|
max_backtrack_stat_ = 0;
|
|
|
|
time_backtrack_ = 0;
|
|
|
|
name_ = name;
|
|
|
|
unit_ = unit;
|
2013-05-22 07:13:11 +02:00
|
|
|
stats = stats_arg;
|
2011-10-06 02:18:46 +02:00
|
|
|
if (seq_backtrack && mode == UDP_MODE)
|
|
|
|
{
|
|
|
|
if (MIN_SEQ_BACKTRACK <= seq_backtrack
|
|
|
|
&& seq_backtrack <= MAX_SEQ_BACKTRACK
|
|
|
|
&& MIN_TIME_BACKTRACK <= time_backtrack
|
|
|
|
&& time_backtrack <= MAX_TIME_BACKTRACK)
|
|
|
|
{
|
|
|
|
seq_backtrack_ = seq_backtrack;
|
|
|
|
time_backtrack_ = time_backtrack;
|
|
|
|
seq_list_.init(seq_backtrack);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
throw packet_id_backtrack_out_of_range();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
seq_list_.init(0);
|
|
|
|
initialized_ = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return true if packet id is ok, or false if
|
|
|
|
* it's a replay.
|
|
|
|
*/
|
2011-11-09 06:52:52 +01:00
|
|
|
bool test(const PacketID& pin, const PacketID::time_t now)
|
2011-10-06 02:18:46 +02:00
|
|
|
{
|
|
|
|
// make sure we were initialized
|
|
|
|
if (!initialized_)
|
|
|
|
throw packet_id_not_initialized();
|
|
|
|
|
|
|
|
// see if we should do an expiration reap pass
|
|
|
|
if (last_reap_ + SEQ_REAP_PERIOD <= now)
|
2011-11-09 06:52:52 +01:00
|
|
|
reap(now);
|
2011-10-06 02:18:46 +02:00
|
|
|
|
2011-12-05 07:11:51 +01:00
|
|
|
// test for invalid packet ID
|
|
|
|
if (!pin.is_valid())
|
2011-10-06 02:18:46 +02:00
|
|
|
{
|
2013-05-22 07:13:11 +02:00
|
|
|
debug_log (Error::PKTID_INVALID, pin, "PID is invalid", 0, now);
|
2011-10-06 02:18:46 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (seq_list_.defined())
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* In backtrack mode, we allow packet reordering subject
|
|
|
|
* to the seq_backtrack and time_backtrack constraints.
|
|
|
|
*
|
|
|
|
* This mode is used with UDP.
|
|
|
|
*/
|
|
|
|
if (pin.time == time_)
|
|
|
|
{
|
|
|
|
/* is packet-id greater than any one we've seen yet? */
|
|
|
|
if (pin.id > id_)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* check packet-id sliding window for original/replay status */
|
|
|
|
const PacketID::id_t diff = id_ - pin.id;
|
2011-09-29 01:12:37 +02:00
|
|
|
|
2011-10-06 02:18:46 +02:00
|
|
|
/* keep track of maximum backtrack seen for debugging purposes */
|
|
|
|
if (diff > max_backtrack_stat_)
|
2011-09-29 01:12:37 +02:00
|
|
|
{
|
2011-10-06 02:18:46 +02:00
|
|
|
max_backtrack_stat_ = diff;
|
2013-05-22 07:13:11 +02:00
|
|
|
debug_log (Error::PKTID_UDP_REPLAY_WINDOW_BACKTRACK, pin, "UDP replay-window backtrack occurred", max_backtrack_stat_, now);
|
2011-09-29 01:12:37 +02:00
|
|
|
}
|
2011-10-06 02:18:46 +02:00
|
|
|
|
|
|
|
if (diff >= seq_list_.size())
|
|
|
|
{
|
2013-05-22 07:13:11 +02:00
|
|
|
debug_log (Error::PKTID_UDP_LARGE_DIFF, pin, "UDP large diff", diff, now);
|
2011-10-06 02:18:46 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-09-29 01:12:37 +02:00
|
|
|
{
|
2011-10-06 02:18:46 +02:00
|
|
|
const PacketID::time_t v = seq_list_[diff];
|
|
|
|
if (v == PacketID::time_t(SEQ_UNSEEN))
|
2011-09-29 01:12:37 +02:00
|
|
|
return true;
|
|
|
|
else
|
|
|
|
{
|
2013-05-22 07:13:11 +02:00
|
|
|
debug_log (Error::PKTID_UDP_REPLAY, pin, "UDP replay", diff, now);
|
2011-09-29 01:12:37 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2011-10-06 02:18:46 +02:00
|
|
|
}
|
|
|
|
else if (pin.time < time_) /* if time goes back, reject */
|
|
|
|
{
|
2013-05-22 07:13:11 +02:00
|
|
|
debug_log (Error::PKTID_UDP_TIME_BACKTRACK, pin, "UDP time backtrack", 0, now);
|
2011-10-06 02:18:46 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else /* time moved forward */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* In non-backtrack mode, all sequence number series must
|
|
|
|
* begin at some number n > 0 and must increment linearly without gaps.
|
|
|
|
*
|
|
|
|
* This mode is used with TCP.
|
|
|
|
*/
|
|
|
|
if (pin.time == time_)
|
|
|
|
{
|
|
|
|
if (pin.id == id_ + 1)
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
{
|
2013-05-22 07:13:11 +02:00
|
|
|
debug_log (Error::PKTID_TCP_OUT_OF_SEQ, pin, "TCP packet ID out of sequence", 0, now);
|
2011-10-06 02:18:46 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (pin.time < time_) /* if time goes back, reject */
|
|
|
|
{
|
2013-05-22 07:13:11 +02:00
|
|
|
debug_log (Error::PKTID_TCP_TIME_BACKTRACK, pin, "TCP time backtrack", 0, now);
|
2011-10-06 02:18:46 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else /* time moved forward */
|
|
|
|
{
|
|
|
|
if (pin.id == 1)
|
|
|
|
return true;
|
|
|
|
else
|
|
|
|
{
|
2013-05-22 07:13:11 +02:00
|
|
|
debug_log (Error::PKTID_TCP_BAD_INITIAL, pin, "bad initial TCP packet ID", 0, now);
|
2011-10-06 02:18:46 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-09-29 01:12:37 +02:00
|
|
|
|
2011-10-06 02:18:46 +02:00
|
|
|
void
|
2011-11-09 06:52:52 +01:00
|
|
|
add(const PacketID& pin, const PacketID::time_t now)
|
2011-10-06 02:18:46 +02:00
|
|
|
{
|
|
|
|
if (!initialized_)
|
|
|
|
throw packet_id_not_initialized();
|
|
|
|
if (seq_list_.defined())
|
|
|
|
{
|
|
|
|
// UDP mode. Decide if we should reset sequence number history list.
|
|
|
|
if (!seq_list_.size() // indicates first pass
|
|
|
|
|| pin.time > time_ // if time value increases, must reset
|
|
|
|
|| (pin.id >= seq_backtrack_ // also, big jumps in pin.id require us to reset
|
|
|
|
&& pin.id - seq_backtrack_ > id_))
|
|
|
|
{
|
|
|
|
time_ = pin.time;
|
|
|
|
id_ = 0;
|
|
|
|
if (pin.id > seq_backtrack_) // if pin.id is large, fast-forward
|
|
|
|
id_ = pin.id - seq_backtrack_;
|
|
|
|
seq_list_.reset();
|
|
|
|
}
|
2011-09-29 01:12:37 +02:00
|
|
|
|
2011-10-06 02:18:46 +02:00
|
|
|
while (id_ < pin.id) // should never iterate more than seq_backtrack_ steps
|
2011-09-29 01:12:37 +02:00
|
|
|
{
|
2011-10-06 02:18:46 +02:00
|
|
|
seq_list_.push(PacketID::time_t(SEQ_UNSEEN));
|
|
|
|
++id_;
|
2011-09-29 01:12:37 +02:00
|
|
|
}
|
2011-10-06 02:18:46 +02:00
|
|
|
|
|
|
|
// remember timestamp of packet ID
|
2011-09-29 01:12:37 +02:00
|
|
|
{
|
2011-10-06 02:18:46 +02:00
|
|
|
const size_t diff = id_ - pin.id;
|
2011-11-09 06:52:52 +01:00
|
|
|
if (diff < seq_list_.size() && now > PacketID::time_t(SEQ_EXPIRED))
|
|
|
|
seq_list_[diff] = now;
|
2011-09-29 01:12:37 +02:00
|
|
|
}
|
2011-10-06 02:18:46 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// TCP mode
|
|
|
|
time_ = pin.time;
|
|
|
|
id_ = pin.id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PacketID read_next(Buffer& buf) const
|
|
|
|
{
|
|
|
|
if (!initialized_)
|
|
|
|
throw packet_id_not_initialized();
|
|
|
|
PacketID pid;
|
|
|
|
pid.read(buf, form_);
|
|
|
|
return pid;
|
|
|
|
}
|
2011-09-29 01:12:37 +02:00
|
|
|
|
2013-05-22 07:13:11 +02:00
|
|
|
#ifdef OPENVPN_INSTRUMENTATION
|
2011-11-09 06:52:52 +01:00
|
|
|
std::string str(const PacketID::time_t now) const
|
2011-10-06 02:18:46 +02:00
|
|
|
{
|
|
|
|
if (!initialized_)
|
|
|
|
throw packet_id_not_initialized();
|
|
|
|
std::ostringstream os;
|
|
|
|
os << name_ << "-" << unit_ << " [";
|
|
|
|
for (size_t i = 0; i < seq_list_.size(); ++i)
|
|
|
|
{
|
|
|
|
char c;
|
|
|
|
const PacketID::time_t v = seq_list_[i];
|
|
|
|
if (v == PacketID::time_t(SEQ_UNSEEN))
|
|
|
|
c = '_';
|
|
|
|
else if (v == PacketID::time_t(SEQ_EXPIRED))
|
|
|
|
c = 'E';
|
|
|
|
else
|
|
|
|
{
|
2011-11-09 06:52:52 +01:00
|
|
|
const int diff = int(now - v);
|
2011-10-06 02:18:46 +02:00
|
|
|
if (diff < 0)
|
|
|
|
c = 'N';
|
|
|
|
else if (diff < 10)
|
|
|
|
c = '0' + diff;
|
|
|
|
else
|
|
|
|
c = '>';
|
|
|
|
}
|
|
|
|
os << c;
|
|
|
|
}
|
|
|
|
os << "] " << time_ << ":" << id_;
|
|
|
|
return os.str();
|
|
|
|
}
|
2011-09-29 01:12:37 +02:00
|
|
|
#endif
|
|
|
|
|
2011-10-06 02:18:46 +02:00
|
|
|
private:
|
|
|
|
/*
|
|
|
|
* Expire sequence numbers which can no longer
|
|
|
|
* be accepted because they would violate
|
|
|
|
* time_backtrack.
|
|
|
|
*/
|
2011-11-09 06:52:52 +01:00
|
|
|
void reap(const PacketID::time_t now)
|
2011-10-06 02:18:46 +02:00
|
|
|
{
|
|
|
|
if (time_backtrack_)
|
|
|
|
{
|
|
|
|
bool expire = false;
|
|
|
|
for (size_t i = 0; i < seq_list_.size(); ++i)
|
|
|
|
{
|
|
|
|
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;
|
2011-11-09 06:52:52 +01:00
|
|
|
if (!expire && t && t + time_backtrack_ < now)
|
2011-10-06 02:18:46 +02:00
|
|
|
expire = true;
|
|
|
|
if (expire)
|
|
|
|
seq_list_[i] = PacketID::time_t(SEQ_EXPIRED);
|
|
|
|
}
|
|
|
|
}
|
2011-11-09 06:52:52 +01:00
|
|
|
last_reap_ = now;
|
2011-10-06 02:18:46 +02:00
|
|
|
}
|
2011-09-29 01:12:37 +02:00
|
|
|
|
2013-05-22 07:13:11 +02:00
|
|
|
void debug_log (const Error::Type err_type, const PacketID& pin, const char *description, const PacketID::id_t info, const PacketID::time_t now) const
|
2011-10-06 02:18:46 +02:00
|
|
|
{
|
2013-05-22 07:13:11 +02:00
|
|
|
#ifdef OPENVPN_INSTRUMENTATION
|
|
|
|
if (stats->verbose())
|
|
|
|
{
|
|
|
|
const std::string text = fmt_info(pin, description, info, now);
|
|
|
|
stats->error(err_type, &text);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
stats->error(err_type);
|
2011-12-17 06:06:39 +01:00
|
|
|
}
|
|
|
|
|
2013-05-22 07:13:11 +02:00
|
|
|
#ifdef OPENVPN_INSTRUMENTATION
|
|
|
|
std::string fmt_info (const PacketID& pin, const char *description, const PacketID::id_t info, const PacketID::time_t now) const
|
2011-12-17 06:06:39 +01:00
|
|
|
{
|
2013-05-22 07:13:11 +02:00
|
|
|
std::ostringstream os;
|
|
|
|
os << description << " pin=[" << pin.time << "," << pin.id << "] info=" << info << " state=" << str(now);
|
|
|
|
return os.str();
|
2011-10-06 02:18:46 +02:00
|
|
|
}
|
2011-12-17 06:06:39 +01:00
|
|
|
#endif
|
|
|
|
|
2011-10-06 02:18:46 +02:00
|
|
|
bool initialized_; /* true if packet_id_init was called */
|
|
|
|
PacketID::id_t id_; /* highest sequence number received */
|
|
|
|
PacketID::time_t time_; /* highest time stamp received */
|
|
|
|
PacketID::time_t last_reap_; /* last call of packet_id_reap */
|
|
|
|
PacketID::id_t seq_backtrack_; /* maximum allowed packet ID backtrack (init parameter) */
|
|
|
|
PacketID::id_t max_backtrack_stat_; /* maximum backtrack seen so far */
|
|
|
|
int time_backtrack_; /* maximum allowed time backtrack (init parameter) */
|
|
|
|
std::string name_; /* name of this object (for debugging) */
|
|
|
|
int unit_; /* unit number of this object (for debugging) */
|
|
|
|
int form_; /* PacketID::LONG_FORM or PacketID::SHORT_FORM */
|
2013-05-22 07:13:11 +02:00
|
|
|
SessionStats::Ptr stats; /* used for error logging */
|
2011-10-06 02:18:46 +02:00
|
|
|
CircList<PacketID::time_t> seq_list_; /* packet-id "memory" */
|
|
|
|
};
|
2011-09-29 01:12:37 +02:00
|
|
|
|
|
|
|
} // namespace openvpn
|
|
|
|
|
|
|
|
#endif // OPENVPN_CRYPTO_PACKET_ID_H
|