mirror of
https://github.com/mfontanini/libtins
synced 2026-01-23 02:35:57 +01:00
Add initial ACK tracking code
This commit is contained in:
@@ -15,6 +15,7 @@ addons:
|
||||
packages:
|
||||
- libpcap-dev
|
||||
- libssl-dev
|
||||
- libboost-all-dev
|
||||
|
||||
before_script:
|
||||
- mkdir build
|
||||
@@ -22,5 +23,9 @@ before_script:
|
||||
- cmake .. -DLIBTINS_ENABLE_CXX11=1
|
||||
- make tests
|
||||
|
||||
before_install:
|
||||
- brew update
|
||||
- brew install boost
|
||||
|
||||
script:
|
||||
- ctest -V
|
||||
@@ -56,7 +56,6 @@ IF(WIN32)
|
||||
ADD_DEFINITIONS(-DNOMINMAX)
|
||||
ENDIF(WIN32)
|
||||
|
||||
|
||||
INCLUDE(ExternalProject)
|
||||
|
||||
# *******************
|
||||
@@ -97,6 +96,22 @@ IF(LIBTINS_ENABLE_DOT11)
|
||||
ENDIF(LIBTINS_ENABLE_WPA2)
|
||||
ENDIF(LIBTINS_ENABLE_DOT11)
|
||||
|
||||
OPTION(LIBTINS_ENABLE_ACK_TRACKER "Enable TCP ACK tracking support" ON)
|
||||
IF(LIBTINS_ENABLE_ACK_TRACKER AND HAVE_CXX11)
|
||||
FIND_PACKAGE(Boost)
|
||||
IF (Boost_FOUND)
|
||||
MESSAGE(STATUS "Enabling TCP ACK tracking support.")
|
||||
INCLUDE_DIRECTORIES(Boost_INCLUDE_DIRS)
|
||||
SET(HAVE_ACK_TRACKER ON)
|
||||
ELSE()
|
||||
MESSAGE(WARNING "Disabling ACK tracking support as boost.icl was not found")
|
||||
SET(HAVE_ACK_TRACKER OFF)
|
||||
ENDIF()
|
||||
ELSE()
|
||||
SET(HAVE_ACK_TRACKER OFF)
|
||||
MESSAGE(STATUS "Disabling ACK tracking support")
|
||||
ENDIF()
|
||||
|
||||
# Use pcap_sendpacket to send l2 packets rather than raw sockets
|
||||
IF(WIN32)
|
||||
SET(USE_PCAP_SENDPACKET_DEFAULT ON)
|
||||
|
||||
@@ -13,4 +13,7 @@
|
||||
/* Use pcap_sendpacket to send l2 packets */
|
||||
#cmakedefine HAVE_PACKET_SENDER_PCAP_SENDPACKET
|
||||
|
||||
/* Have TCP ACK tracking */
|
||||
#cmakedefine HAVE_ACK_TRACKER
|
||||
|
||||
#endif // TINS_CONFIG_H
|
||||
|
||||
@@ -200,6 +200,17 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Exception thrown when a feature has been disabled
|
||||
* at compile time.
|
||||
*/
|
||||
class feature_disabled : public exception_base {
|
||||
public:
|
||||
const char* what() const throw() {
|
||||
return "Feature disabled";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Exception thrown when a payload is too large to fit
|
||||
* into a PDUOption.
|
||||
|
||||
@@ -177,6 +177,9 @@ bool decrement(HWAddress<n>& addr) {
|
||||
return decrement_buffer(addr);
|
||||
}
|
||||
|
||||
// Compares sequence numbers as defined by RFC 1982.
|
||||
int seq_compare(uint32_t seq1, uint32_t seq2);
|
||||
|
||||
IPv4Address last_address_from_mask(IPv4Address addr, IPv4Address mask);
|
||||
IPv6Address last_address_from_mask(IPv6Address addr, const IPv6Address& mask);
|
||||
template<size_t n>
|
||||
|
||||
147
include/tins/tcp_ip/ack_tracker.h
Normal file
147
include/tins/tcp_ip/ack_tracker.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Matias Fontanini
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TINS_TCP_IP_ACK_TRACKER_H
|
||||
#define TINS_TCP_IP_ACK_TRACKER_H
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#ifdef HAVE_ACK_TRACKER
|
||||
|
||||
#include <vector>
|
||||
#include <boost/icl/interval_set.hpp>
|
||||
|
||||
namespace Tins {
|
||||
|
||||
class PDU;
|
||||
|
||||
namespace TCPIP {
|
||||
|
||||
/**
|
||||
* \brief Represents an acknowledged segment range
|
||||
*
|
||||
* The interval represented by this range is a closed interval [first, last].
|
||||
*/
|
||||
class AckedRange {
|
||||
public:
|
||||
typedef boost::icl::discrete_interval<uint32_t> interval_type;
|
||||
|
||||
/**
|
||||
* \brief Constructs an acked range
|
||||
*
|
||||
* \param first The first acked byte
|
||||
* \param last The last acked byte (inclusive)
|
||||
*/
|
||||
AckedRange(uint32_t first, uint32_t last);
|
||||
|
||||
/**
|
||||
* \brief Gets the next acked interval in this range
|
||||
*
|
||||
* If has_next() == false, then this returns an empty interval
|
||||
*/
|
||||
interval_type next();
|
||||
|
||||
/**
|
||||
* Indicates whether there is still some non-consumed acked-interval in this
|
||||
* range
|
||||
*/
|
||||
bool has_next() const;
|
||||
|
||||
/**
|
||||
* Gets the first index acked by this range
|
||||
*/
|
||||
uint32_t first() const;
|
||||
|
||||
/**
|
||||
* Gets the last index acked by this range
|
||||
*/
|
||||
uint32_t last() const;
|
||||
private:
|
||||
uint32_t first_;
|
||||
uint32_t last_;
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Allows tracking acknowledged intervals in a TCP stream
|
||||
*/
|
||||
class AckTracker {
|
||||
public:
|
||||
/**
|
||||
* The type used to store ACKed intervals
|
||||
*/
|
||||
typedef boost::icl::interval_set<uint32_t> interval_set_type;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
AckTracker();
|
||||
|
||||
/**
|
||||
* \brief Construct an instance using some attributes
|
||||
*
|
||||
* \param intial_ack The initial ACK number to use
|
||||
* \param use_sack Indicate whether to use Selective ACKs to track ACK numbers
|
||||
*/
|
||||
AckTracker(uint32_t initial_ack, bool use_sack = true);
|
||||
|
||||
/**
|
||||
* \brief Process a packet
|
||||
*/
|
||||
void process_packet(const PDU& packet);
|
||||
|
||||
/**
|
||||
* \brief Indicates whether Selective ACKs should be processed
|
||||
*/
|
||||
void use_sack();
|
||||
|
||||
/**
|
||||
* Retrieves the current ACK number in this tracker
|
||||
*/
|
||||
uint32_t ack_number() const;
|
||||
|
||||
/**
|
||||
* \brief Retrieves all acked intervals by Selective ACKs
|
||||
*/
|
||||
const interval_set_type& acked_intervals() const;
|
||||
private:
|
||||
void process_sack(const std::vector<uint32_t>& sack);
|
||||
void cleanup_sacked_intervals(uint32_t old_ack, uint32_t new_ack);
|
||||
|
||||
interval_set_type acked_intervals_;
|
||||
uint32_t ack_number_;
|
||||
bool use_sack_;
|
||||
};
|
||||
|
||||
} // TCPIP
|
||||
} // Tins
|
||||
|
||||
#endif // HAVE_ACK_TRACKER
|
||||
|
||||
#endif // TINS_TCP_IP_ACK_TRACKER_H
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <functional>
|
||||
#include <stdint.h>
|
||||
#include "../macros.h"
|
||||
#include "ack_tracker.h"
|
||||
#include "../hw_address.h"
|
||||
|
||||
namespace Tins {
|
||||
@@ -267,16 +268,31 @@ public:
|
||||
* \brief Indicates whether this Flow supports selective acknowledgements
|
||||
*/
|
||||
bool sack_permitted() const;
|
||||
|
||||
/**
|
||||
* \brief Enables tracking of ACK numbers
|
||||
*
|
||||
* This requires having the boost.icl library. If the library is not installed
|
||||
* or ACK tracking was disabled when compiling the library, then this method
|
||||
* will throw an exception.
|
||||
*/
|
||||
void enable_ack_tracking();
|
||||
|
||||
/**
|
||||
* \brief Indicates whether ACK number tracking is enabled
|
||||
*/
|
||||
bool ack_tracking_enabled() const;
|
||||
private:
|
||||
// Compress all flags into just one struct using bitfields
|
||||
struct flags {
|
||||
flags() : ignore_data_packets(0), sack_permitted(0) {
|
||||
flags() : ignore_data_packets(0), sack_permitted(0), ack_tracking(0) {
|
||||
|
||||
}
|
||||
|
||||
uint32_t is_v6:1,
|
||||
ignore_data_packets:1,
|
||||
sack_permitted:1;
|
||||
sack_permitted:1,
|
||||
ack_tracking:1;
|
||||
};
|
||||
|
||||
void store_payload(uint32_t seq, payload_type payload);
|
||||
@@ -295,6 +311,9 @@ private:
|
||||
State state_;
|
||||
int mss_;
|
||||
flags flags_;
|
||||
#ifdef HAVE_ACK_TRACKER
|
||||
AckTracker ack_tracker_;
|
||||
#endif // HAVE_ACK_TRACKER
|
||||
};
|
||||
|
||||
} // TCPIP
|
||||
|
||||
@@ -334,6 +334,18 @@ public:
|
||||
* \param value The value to be set for this property
|
||||
*/
|
||||
void auto_cleanup_payloads(bool value);
|
||||
|
||||
/**
|
||||
* Enables tracking of acknowledged segments
|
||||
*
|
||||
* \sa Flow::enable_ack_tracking
|
||||
*/
|
||||
void enable_ack_tracking();
|
||||
|
||||
/**
|
||||
* \brief Indicates whether ACK number tracking is enabled for this stream
|
||||
*/
|
||||
bool ack_tracking_enabled() const;
|
||||
private:
|
||||
static Flow extract_client_flow(const PDU& packet);
|
||||
static Flow extract_server_flow(const PDU& packet);
|
||||
|
||||
@@ -53,6 +53,7 @@ ADD_LIBRARY(
|
||||
snap.cpp
|
||||
sniffer.cpp
|
||||
tcp.cpp
|
||||
tcp_ip/ack_tracker.cpp
|
||||
tcp_ip/flow.cpp
|
||||
tcp_ip/stream.cpp
|
||||
tcp_ip/stream_follower.cpp
|
||||
|
||||
@@ -349,6 +349,20 @@ bool decrement(IPv6Address& addr) {
|
||||
return decrement_buffer(addr);
|
||||
}
|
||||
|
||||
int seq_compare(uint32_t seq1, uint32_t seq2) {
|
||||
// As defined by RFC 1982 - 2 ^ (SERIAL_BITS - 1)
|
||||
static const uint32_t seq_number_diff = 2147483648U;
|
||||
if (seq1 == seq2) {
|
||||
return 0;
|
||||
}
|
||||
if (seq1 < seq2) {
|
||||
return (seq2 - seq1 < seq_number_diff) ? -1 : 1;
|
||||
}
|
||||
else {
|
||||
return (seq1 - seq2 > seq_number_diff) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
IPv4Address last_address_from_mask(IPv4Address addr, IPv4Address mask) {
|
||||
uint32_t addr_int = Endian::be_to_host<uint32_t>(addr),
|
||||
mask_int = Endian::be_to_host<uint32_t>(mask);
|
||||
|
||||
146
src/tcp_ip/ack_tracker.cpp
Normal file
146
src/tcp_ip/ack_tracker.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Matias Fontanini
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tcp_ip/ack_tracker.h"
|
||||
|
||||
#ifdef HAVE_ACK_TRACKER
|
||||
|
||||
#include <limits>
|
||||
#include "tcp.h"
|
||||
#include "internals.h"
|
||||
|
||||
using std::vector;
|
||||
using std::numeric_limits;
|
||||
|
||||
using Tins::Internals::seq_compare;
|
||||
|
||||
namespace Tins {
|
||||
namespace TCPIP {
|
||||
|
||||
// AckedRange
|
||||
|
||||
AckedRange::AckedRange(uint32_t first, uint32_t last)
|
||||
: first_(first), last_(last) {
|
||||
|
||||
}
|
||||
|
||||
AckedRange::interval_type AckedRange::next() {
|
||||
uint32_t interval_first = first_;
|
||||
// Regular case
|
||||
if (first_ <= last_) {
|
||||
first_ = last_ + 1;
|
||||
return interval_type::closed(interval_first, last_);
|
||||
}
|
||||
else {
|
||||
// Range wraps around
|
||||
first_ = 0;
|
||||
return interval_type::closed(interval_first, numeric_limits<uint32_t>::max());
|
||||
}
|
||||
}
|
||||
|
||||
bool AckedRange::has_next() const {
|
||||
return seq_compare(first_, last_) <= 0;
|
||||
}
|
||||
|
||||
uint32_t AckedRange::first() const {
|
||||
return first_;
|
||||
}
|
||||
|
||||
uint32_t AckedRange::last() const {
|
||||
return last_;
|
||||
}
|
||||
|
||||
// AckTracker
|
||||
|
||||
AckTracker::AckTracker()
|
||||
: ack_number_(0), use_sack_(false) {
|
||||
|
||||
}
|
||||
|
||||
AckTracker::AckTracker(uint32_t initial_ack, bool use_sack)
|
||||
: ack_number_(initial_ack), use_sack_(use_sack) {
|
||||
|
||||
}
|
||||
|
||||
void AckTracker::process_packet(const PDU& packet) {
|
||||
const TCP* tcp = packet.find_pdu<TCP>();
|
||||
if (!tcp) {
|
||||
return;
|
||||
}
|
||||
if (seq_compare(tcp->ack_seq(), ack_number_) > 0) {
|
||||
cleanup_sacked_intervals(ack_number_, tcp->ack_seq());
|
||||
ack_number_ = tcp->ack_seq();
|
||||
}
|
||||
if (use_sack_) {
|
||||
const TCP::option* sack_option = tcp->search_option(TCP::SACK);
|
||||
if (sack_option) {
|
||||
TCP::sack_type sack = sack_option->to<TCP::sack_type>();
|
||||
process_sack(sack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AckTracker::process_sack(const vector<uint32_t>& sack) {
|
||||
for (size_t i = 1; i < sack.size(); i += 2) {
|
||||
// Left edge must be lower than right edge
|
||||
if (seq_compare(sack[i - 1], sack[i]) < 0) {
|
||||
AckedRange range(sack[i - 1], sack[i] - 1);
|
||||
// If this range starts after our current ack number
|
||||
if (seq_compare(range.first(), ack_number_) > 0) {
|
||||
while (range.has_next()) {
|
||||
acked_intervals_.insert(range.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AckTracker::cleanup_sacked_intervals(uint32_t old_ack, uint32_t new_ack) {
|
||||
AckedRange range(old_ack, new_ack);
|
||||
while (range.has_next()) {
|
||||
acked_intervals_.erase(range.next());
|
||||
}
|
||||
}
|
||||
|
||||
void AckTracker::use_sack() {
|
||||
use_sack_ = true;
|
||||
}
|
||||
|
||||
uint32_t AckTracker::ack_number() const {
|
||||
return ack_number_;
|
||||
}
|
||||
|
||||
const AckTracker::interval_set_type& AckTracker::acked_intervals() const {
|
||||
return acked_intervals_;
|
||||
}
|
||||
|
||||
} // TCPIP
|
||||
} // Tins
|
||||
|
||||
#endif // HAVE_ACK_TRACKER
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "ip.h"
|
||||
#include "ipv6.h"
|
||||
#include "rawpdu.h"
|
||||
#include "internals.h"
|
||||
#include "exceptions.h"
|
||||
#include "memory_helpers.h"
|
||||
|
||||
@@ -53,26 +54,11 @@ using std::swap;
|
||||
|
||||
using Tins::Memory::OutputMemoryStream;
|
||||
using Tins::Memory::InputMemoryStream;
|
||||
using Tins::Internals::seq_compare;
|
||||
|
||||
namespace Tins {
|
||||
namespace TCPIP {
|
||||
|
||||
// As defined by RFC 1982 - 2 ^ (SERIAL_BITS - 1)
|
||||
static const uint32_t seq_number_diff = 2147483648U;
|
||||
|
||||
// Compares sequence numbers as defined by RFC 1982.
|
||||
int seq_compare(uint32_t seq1, uint32_t seq2) {
|
||||
if (seq1 == seq2) {
|
||||
return 0;
|
||||
}
|
||||
if (seq1 < seq2) {
|
||||
return (seq2 - seq1 < seq_number_diff) ? -1 : 1;
|
||||
}
|
||||
else {
|
||||
return (seq1 - seq2 > seq_number_diff) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
Flow::Flow(const IPv4Address& dest_address, uint16_t dest_port,
|
||||
uint32_t sequence_number)
|
||||
: seq_number_(sequence_number), dest_port_(dest_port) {
|
||||
@@ -111,6 +97,11 @@ void Flow::process_packet(PDU& pdu) {
|
||||
// Update the internal state first
|
||||
if (tcp) {
|
||||
update_state(*tcp);
|
||||
#ifdef HAVE_ACK_TRACKER
|
||||
if (flags_.ack_tracking) {
|
||||
ack_tracker_.process_packet(*tcp);
|
||||
}
|
||||
#endif // HAVE_ACK_TRACKER
|
||||
}
|
||||
if (flags_.ignore_data_packets) {
|
||||
return;
|
||||
@@ -316,6 +307,18 @@ bool Flow::sack_permitted() const {
|
||||
return flags_.sack_permitted;
|
||||
}
|
||||
|
||||
void Flow::enable_ack_tracking() {
|
||||
#ifdef HAVE_ACK_TRACKER
|
||||
flags_.ack_tracking = 1;
|
||||
#else
|
||||
throw feature_disabled();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Flow::ack_tracking_enabled() const {
|
||||
return flags_.ack_tracking;
|
||||
}
|
||||
|
||||
} // TCPIP
|
||||
} // Tins
|
||||
|
||||
|
||||
@@ -256,6 +256,15 @@ void Stream::auto_cleanup_payloads(bool value) {
|
||||
auto_cleanup_ = value;
|
||||
}
|
||||
|
||||
void Stream::enable_ack_tracking() {
|
||||
client_flow().enable_ack_tracking();
|
||||
server_flow().enable_ack_tracking();
|
||||
}
|
||||
|
||||
bool Stream::ack_tracking_enabled() const {
|
||||
return client_flow().ack_tracking_enabled() && server_flow().ack_tracking_enabled();
|
||||
}
|
||||
|
||||
void Stream::on_client_flow_data(const Flow& /*flow*/) {
|
||||
if (on_client_data_callback_) {
|
||||
on_client_data_callback_(*this);
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
#include "rawpdu.h"
|
||||
#include "packet.h"
|
||||
#include "utils.h"
|
||||
#include "config.h"
|
||||
#ifdef HAVE_ACK_TRACKER
|
||||
#include "tcp_ip/ack_tracker.h"
|
||||
#endif // HAVE_ACK_TRACKER
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
@@ -508,4 +512,171 @@ TEST_F(FlowTest, StreamFollower_FollowStream) {
|
||||
EXPECT_EQ(payload, merge_chunks(stream_client_payload_chunks));
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_ACK_TRACKER
|
||||
|
||||
using namespace boost::icl;
|
||||
|
||||
class AckTrackerTest : public testing::Test {
|
||||
public:
|
||||
typedef AckedRange::interval_type interval_type;
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
vector<uint32_t> make_sack() {
|
||||
return vector<uint32_t>();
|
||||
}
|
||||
|
||||
template <typename... SackEdges>
|
||||
vector<uint32_t> make_sack(pair<uint32_t, uint32_t> head, SackEdges&&... tail) {
|
||||
vector<uint32_t> output = make_sack(tail...);
|
||||
output.push_back(head.first);
|
||||
output.push_back(head.second);
|
||||
return output;
|
||||
}
|
||||
|
||||
TCP make_tcp_ack(uint32_t ack_number) {
|
||||
TCP output;
|
||||
output.ack_seq(ack_number);
|
||||
return output;
|
||||
}
|
||||
|
||||
template <typename... SackEdges>
|
||||
TCP make_tcp_ack(uint32_t ack_number, SackEdges&&... rest) {
|
||||
TCP output = make_tcp_ack(ack_number);
|
||||
vector<uint32_t> sack = make_sack(rest...);
|
||||
output.sack(sack);
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
ostream& operator<<(ostream& stream, const AckTrackerTest::interval_type& interval) {
|
||||
stream << ((interval.bounds() == interval_bounds::left_open()) ? "(" : "[");
|
||||
stream << interval.lower() << ", " << interval.upper();
|
||||
stream << ((interval.bounds() == interval_bounds::right_open()) ? ")" : "]");
|
||||
return stream;
|
||||
}
|
||||
|
||||
TEST_F(AckTrackerTest, AckedRange_1) {
|
||||
AckedRange range(0, 100);
|
||||
EXPECT_TRUE(range.has_next());
|
||||
EXPECT_EQ(interval_type::closed(0, 100), range.next());
|
||||
EXPECT_FALSE(range.has_next());
|
||||
}
|
||||
|
||||
TEST_F(AckTrackerTest, AckedRange_2) {
|
||||
AckedRange range(2, 3);
|
||||
EXPECT_TRUE(range.has_next());
|
||||
EXPECT_EQ(interval_type::closed(2, 3), range.next());
|
||||
EXPECT_FALSE(range.has_next());
|
||||
}
|
||||
|
||||
TEST_F(AckTrackerTest, AckedRange_3) {
|
||||
AckedRange range(0, 0);
|
||||
EXPECT_TRUE(range.has_next());
|
||||
EXPECT_EQ(interval_type::right_open(0, 1), range.next());
|
||||
EXPECT_FALSE(range.has_next());
|
||||
}
|
||||
|
||||
TEST_F(AckTrackerTest, AckedRange_4) {
|
||||
uint32_t maximum = numeric_limits<uint32_t>::max();
|
||||
AckedRange range(maximum, maximum);
|
||||
EXPECT_TRUE(range.has_next());
|
||||
EXPECT_EQ(interval_type::left_open(maximum - 1, maximum), range.next());
|
||||
EXPECT_FALSE(range.has_next());
|
||||
}
|
||||
|
||||
TEST_F(AckTrackerTest, AckedRange_WrapAround) {
|
||||
uint32_t first = numeric_limits<uint32_t>::max() - 5;
|
||||
AckedRange range(first, 100);
|
||||
EXPECT_TRUE(range.has_next());
|
||||
EXPECT_EQ(
|
||||
interval_type::closed(first, numeric_limits<uint32_t>::max()),
|
||||
range.next()
|
||||
);
|
||||
EXPECT_TRUE(range.has_next());
|
||||
EXPECT_EQ(interval_type::closed(0, 100), range.next());
|
||||
EXPECT_FALSE(range.has_next());
|
||||
}
|
||||
|
||||
TEST_F(AckTrackerTest, AckingTcp1) {
|
||||
AckTracker tracker(0, false);
|
||||
EXPECT_EQ(0, tracker.ack_number());
|
||||
tracker.process_packet(make_tcp_ack(100));
|
||||
EXPECT_EQ(100, tracker.ack_number());
|
||||
tracker.process_packet(make_tcp_ack(50));
|
||||
EXPECT_EQ(100, tracker.ack_number());
|
||||
tracker.process_packet(make_tcp_ack(150));
|
||||
EXPECT_EQ(150, tracker.ack_number());
|
||||
tracker.process_packet(make_tcp_ack(200));
|
||||
EXPECT_EQ(200, tracker.ack_number());
|
||||
}
|
||||
|
||||
TEST_F(AckTrackerTest, AckingTcp2) {
|
||||
uint32_t maximum = numeric_limits<uint32_t>::max();
|
||||
AckTracker tracker(maximum - 10, false);
|
||||
EXPECT_EQ(maximum - 10, tracker.ack_number());
|
||||
tracker.process_packet(make_tcp_ack(maximum - 3));
|
||||
EXPECT_EQ(maximum - 3, tracker.ack_number());
|
||||
tracker.process_packet(make_tcp_ack(maximum));
|
||||
EXPECT_EQ(maximum, tracker.ack_number());
|
||||
tracker.process_packet(make_tcp_ack(5));
|
||||
EXPECT_EQ(5, tracker.ack_number());
|
||||
}
|
||||
|
||||
TEST_F(AckTrackerTest, AckingTcp3) {
|
||||
uint32_t maximum = numeric_limits<uint32_t>::max();
|
||||
AckTracker tracker(maximum - 10, false);
|
||||
tracker.process_packet(make_tcp_ack(5));
|
||||
EXPECT_EQ(5, tracker.ack_number());
|
||||
}
|
||||
|
||||
TEST_F(AckTrackerTest, AckingTcp_Sack1) {
|
||||
AckTracker tracker(0, true);
|
||||
tracker.process_packet(make_tcp_ack(0, make_pair(2, 5), make_pair(9, 11)));
|
||||
EXPECT_EQ(3 + 2, tracker.acked_intervals().size());
|
||||
|
||||
tracker.process_packet(make_tcp_ack(9));
|
||||
EXPECT_EQ(1, tracker.acked_intervals().size());
|
||||
|
||||
tracker.process_packet(make_tcp_ack(15));
|
||||
EXPECT_EQ(0, tracker.acked_intervals().size());
|
||||
}
|
||||
|
||||
TEST_F(AckTrackerTest, AckingTcp_Sack2) {
|
||||
uint32_t maximum = numeric_limits<uint32_t>::max();
|
||||
AckTracker tracker(maximum - 10, true);
|
||||
tracker.process_packet(make_tcp_ack(
|
||||
maximum - 10,
|
||||
make_pair(maximum - 3, maximum),
|
||||
make_pair(0, 10)
|
||||
));
|
||||
EXPECT_EQ(3 + 10, tracker.acked_intervals().size());
|
||||
|
||||
tracker.process_packet(make_tcp_ack(maximum - 2));
|
||||
EXPECT_EQ(1 + 10, tracker.acked_intervals().size());
|
||||
|
||||
tracker.process_packet(make_tcp_ack(5));
|
||||
EXPECT_EQ(4, tracker.acked_intervals().size());
|
||||
|
||||
tracker.process_packet(make_tcp_ack(15));
|
||||
EXPECT_EQ(0, tracker.acked_intervals().size());
|
||||
}
|
||||
|
||||
TEST_F(AckTrackerTest, AckingTcp_Sack3) {
|
||||
uint32_t maximum = numeric_limits<uint32_t>::max();
|
||||
AckTracker tracker(maximum - 10, true);
|
||||
tracker.process_packet(make_tcp_ack(
|
||||
maximum - 10,
|
||||
make_pair(maximum - 3, 5)
|
||||
));
|
||||
EXPECT_EQ(9, tracker.acked_intervals().size());
|
||||
|
||||
tracker.process_packet(make_tcp_ack(maximum));
|
||||
EXPECT_EQ(5, tracker.acked_intervals().size());
|
||||
}
|
||||
|
||||
#endif // HAVE_ACK_TRACKER
|
||||
|
||||
#endif // TINS_IS_CXX11
|
||||
|
||||
Reference in New Issue
Block a user