1
0
mirror of https://github.com/mfontanini/libtins synced 2026-01-23 02:35:57 +01:00

Track Stream timestamps and add timeout to StreamFollower

This commit is contained in:
Matias Fontanini
2016-02-11 21:18:48 -08:00
parent 85d7401520
commit 20a3868e82
10 changed files with 789 additions and 499 deletions

View File

@@ -148,11 +148,19 @@ public:
/**
* \brief Constructs a Packet from a PDU* and a Timestamp.
*
* The PDU* is cloned using PDU::clone.
* The PDU is cloned using PDU::clone.
*/
Packet(const PDU* apdu, const Timestamp& tstamp)
: pdu_(apdu->clone()), ts_(tstamp) { }
/**
* \brief Constructs a Packet from a PDU& and a Timestamp.
*
* The PDU is cloned using PDU::clone.
*/
Packet(const PDU& apdu, const Timestamp& tstamp)
: pdu_(apdu.clone()), ts_(tstamp) { }
/**
* \brief Constructs a Packet from a PDU* and a Timestamp.
*

298
include/tins/tcp_ip/flow.h Normal file
View File

@@ -0,0 +1,298 @@
/*
* 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_FLOW_H
#define TINS_TCP_IP_FLOW_H
#include "../cxxstd.h"
// This classes use C++11 features
#if TINS_IS_CXX11
#include <vector>
#include <array>
#include <map>
#include <functional>
#include <stdint.h>
#include "../macros.h"
#include "../hw_address.h"
namespace Tins {
class PDU;
class TCP;
class IPv4Address;
class IPv6Address;
namespace TCPIP {
/**
* \brief Represents an unidirectional TCP flow between 2 endpoints
*
* This class will keep the state for all the traffic sent by
* one of the peers in a TCP connection. This contains the sequence number,
* payload ready to be read and buffered payload, along with some other
* properties of the flow.
*
* A TCP stream (see class Stream) is made out of 2 Flows, so you should
* probably have a look at that class first.
*
* You shouldn't normally need to interact with this class. Stream already
* provides proxys to most of its Flow's attributes.
*/
class TINS_API Flow {
public:
/**
* \brief Enum that indicates the state of this flow.
*
* Note that although similar, this is not mapped to a TCP state-machine
* state. This is mostly used internally to know which packets the flow is
* expecting and to know when it's done sending data.
*/
enum State {
UNKNOWN,
SYN_SENT,
ESTABLISHED,
FIN_SENT,
RST_SENT
};
/**
* The type used to store the payload
*/
typedef std::vector<uint8_t> payload_type;
/**
* The type used to store the buffered payload
*/
typedef std::map<uint32_t, payload_type> buffered_payload_type;
/**
* The type used to store the callback called when new data is available
*/
typedef std::function<void(Flow&)> data_available_callback_type;
/**
* \brief The type used to store the callback called when data is buffered
*
* The arguments are the flow, the sequence number and payload that will
* be buffered.
*/
typedef std::function<void(Flow&,
uint32_t,
const payload_type&)> out_of_order_callback_type;
/**
* Construct a Flow from an IPv4 address
*
* \param dst_address This flow's destination address
* \param dst_port This flow's destination port
* \param sequence_number The initial sequence number to be used
*/
Flow(const IPv4Address& dst_address, uint16_t dst_port,
uint32_t sequence_number);
/**
* Construct a Flow from an IPv6 address
*
* \param dst_address This flow's destination address
* \param dst_port This flow's destination port
* \param sequence_number The initial sequence number to be used
*/
Flow(const IPv6Address& dst_address, uint16_t dst_port,
uint32_t sequence_number);
/**
* \brief Sets the callback that will be executed when data is readable
*
* Whenever this flow has readable data, this callback will be executed.
* By readable, this means that there's non-out-of-order data captured.
*
* \param callback The callback to be executed
*/
void data_callback(const data_available_callback_type& callback);
/**
* \brief Sets the callback that will be executed when out of order data arrives
*
* Whenever this flow receives out-of-order data, this callback will be
* executed.
*
* \param callback The callback to be executed
*/
void out_of_order_callback(const out_of_order_callback_type& callback);
/**
* \brief Processes a packet.
*
* If this packet contains data and starts or overlaps with the current
* sequence number, then the data will be appended to this flow's payload
* and the data_callback will be executed.
*
* If this packet contains out-of-order data, it will be buffered and the
* buffering_callback will be executed.
*
* \param pdu The packet to be processed
* \sa Flow::data_callback
* \sa Flow::buffering_callback
*/
void process_packet(PDU& pdu);
/**
* Indicates whether this flow uses IPv6 addresses
*/
bool is_v6() const;
/**
* \brief Indicates whether this flow is finished
*
* A finished is considered to be finished if either it sent a
* packet with the FIN or RST flags on.
*/
bool is_finished() const;
/**
* \brief Indicates whether a packet belongs to this flow
*
* Since Flow represents a unidirectional stream, this will only check
* the destination endpoint and not the source one.
*
* \param packet The packet to be checked
*/
bool packet_belongs(const PDU& packet) const;
/**
* \brief Getter for the IPv4 destination address
*
* Note that it's only safe to execute this method if is_v6() == false
*/
IPv4Address dst_addr_v4() const;
/**
* \brief Getter for the IPv6 destination address
*
* Note that it's only safe to execute this method if is_v6() == true
*/
IPv6Address dst_addr_v6() const;
/**
* Getter for this flow's destination port
*/
uint16_t dport() const;
/**
* Getter for this flow's payload (const)
*/
const payload_type& payload() const;
/**
* Getter for this flow's destination port
*/
payload_type& payload();
/**
* Getter for this flow's state
*/
State state() const;
/**
* Getter for this flow's sequence number
*/
uint32_t sequence_number() const;
/**
* Getter for this flow's buffered payload (const)
*/
const buffered_payload_type& buffered_payload() const;
/**
* Getter for this flow's buffered payload
*/
buffered_payload_type& buffered_payload();
/**
* Sets the state of this flow
*
* \param new_state The new state of this flow
*/
void state(State new_state);
/**
* \brief Sets whether this flow should ignore data packets
*
* If the data packets are ignored then the flow will just be
* followed to keep track of its state.
*/
void ignore_data_packets();
/**
* \brief Returns the MSS for this Flow.
*
* If the MSS option wasn't provided by the peer, -1 is returned
*/
int mss() const;
/**
* \brief Indicates whether this Flow supports selective acknowledgements
*/
bool sack_permitted() const;
private:
// Compress all flags into just one struct using bitfields
struct flags {
flags() : ignore_data_packets(0), sack_permitted(0) {
}
uint32_t is_v6:1,
ignore_data_packets:1,
sack_permitted:1;
};
void store_payload(uint32_t seq, payload_type payload);
buffered_payload_type::iterator erase_iterator(buffered_payload_type::iterator iter);
void update_state(const TCP& tcp);
payload_type payload_;
buffered_payload_type buffered_payload_;
uint32_t seq_number_;
std::array<uint8_t, 16> dest_address_;
uint16_t dest_port_;
data_available_callback_type on_data_callback_;
out_of_order_callback_type on_out_of_order_callback_;
State state_;
int mss_;
flags flags_;
};
} // TCPIP
} // TINS
#endif // TINS_IS_CXX11
#endif // TINS_TCP_IP_FLOW_H

View File

@@ -39,9 +39,11 @@
#include <array>
#include <map>
#include <functional>
#include <chrono>
#include <stdint.h>
#include "../macros.h"
#include "../hw_address.h"
#include "flow.h"
namespace Tins {
@@ -52,238 +54,6 @@ class IPv6Address;
namespace TCPIP {
/**
* \brief Represents an unidirectional TCP flow between 2 endpoints
*
* This class will keep the state for all the traffic sent by
* one of the peers in a TCP connection. This contains the sequence number,
* payload ready to be read and buffered payload, along with some other
* properties of the flow.
*
* A TCP stream (see class Stream) is made out of 2 Flows, so you should
* probably have a look at that class first.
*
* You shouldn't normally need to interact with this class. Stream already
* provides proxys to most of its Flow's attributes.
*/
class TINS_API Flow {
public:
/**
* \brief Enum that indicates the state of this flow.
*
* Note that although similar, this is not mapped to a TCP state-machine
* state. This is mostly used internally to know which packets the flow is
* expecting and to know when it's done sending data.
*/
enum State {
UNKNOWN,
SYN_SENT,
ESTABLISHED,
FIN_SENT,
RST_SENT
};
/**
* The type used to store the payload
*/
typedef std::vector<uint8_t> payload_type;
/**
* The type used to store the buffered payload
*/
typedef std::map<uint32_t, payload_type> buffered_payload_type;
/**
* The type used to store the callback called when new data is available
*/
typedef std::function<void(Flow&)> data_available_callback_type;
/**
* \brief The type used to store the callback called when data is buffered
*
* The arguments are the flow, the sequence number and payload that will
* be buffered.
*/
typedef std::function<void(Flow&,
uint32_t,
const payload_type&)> out_of_order_callback_type;
/**
* Construct a Flow from an IPv4 address
*
* \param dst_address This flow's destination address
* \param dst_port This flow's destination port
* \param sequence_number The initial sequence number to be used
*/
Flow(const IPv4Address& dst_address, uint16_t dst_port,
uint32_t sequence_number);
/**
* Construct a Flow from an IPv6 address
*
* \param dst_address This flow's destination address
* \param dst_port This flow's destination port
* \param sequence_number The initial sequence number to be used
*/
Flow(const IPv6Address& dst_address, uint16_t dst_port,
uint32_t sequence_number);
/**
* \brief Sets the callback that will be executed when data is readable
*
* Whenever this flow has readable data, this callback will be executed.
* By readable, this means that there's non-out-of-order data captured.
*
* \param callback The callback to be executed
*/
void data_callback(const data_available_callback_type& callback);
/**
* \brief Sets the callback that will be executed when out of order data arrives
*
* Whenever this flow receives out-of-order data, this callback will be
* executed.
*
* \param callback The callback to be executed
*/
void out_of_order_callback(const out_of_order_callback_type& callback);
/**
* \brief Processes a packet.
*
* If this packet contains data and starts or overlaps with the current
* sequence number, then the data will be appended to this flow's payload
* and the data_callback will be executed.
*
* If this packet contains out-of-order data, it will be buffered and the
* buffering_callback will be executed.
*
* \param pdu The packet to be processed
* \sa Flow::data_callback
* \sa Flow::buffering_callback
*/
void process_packet(PDU& pdu);
/**
* Indicates whether this flow uses IPv6 addresses
*/
bool is_v6() const;
/**
* \brief Indicates whether this flow is finished
*
* A finished is considered to be finished if either it sent a
* packet with the FIN or RST flags on.
*/
bool is_finished() const;
/**
* \brief Indicates whether a packet belongs to this flow
*
* Since Flow represents a unidirectional stream, this will only check
* the destination endpoint and not the source one.
*
* \param packet The packet to be checked
*/
bool packet_belongs(const PDU& packet) const;
/**
* \brief Getter for the IPv4 destination address
*
* Note that it's only safe to execute this method if is_v6() == false
*/
IPv4Address dst_addr_v4() const;
/**
* \brief Getter for the IPv6 destination address
*
* Note that it's only safe to execute this method if is_v6() == true
*/
IPv6Address dst_addr_v6() const;
/**
* Getter for this flow's destination port
*/
uint16_t dport() const;
/**
* Getter for this flow's payload (const)
*/
const payload_type& payload() const;
/**
* Getter for this flow's destination port
*/
payload_type& payload();
/**
* Getter for this flow's state
*/
State state() const;
/**
* Getter for this flow's sequence number
*/
uint32_t sequence_number() const;
/**
* Getter for this flow's buffered payload (const)
*/
const buffered_payload_type& buffered_payload() const;
/**
* Getter for this flow's buffered payload
*/
buffered_payload_type& buffered_payload();
/**
* Sets the state of this flow
*
* \param new_state The new state of this flow
*/
void state(State new_state);
/**
* \brief Sets whether this flow should ignore data packets
*
* If the data packets are ignored then the flow will just be
* followed to keep track of its state.
*/
void ignore_data_packets();
/**
* \brief Returns the MSS for this Flow.
*
* If the MSS option wasn't provided by the peer, -1 is returned
*/
int mss() const;
private:
// Compress all flags into just one struct using bitfields
struct flags {
flags() : ignore_data_packets(0) {
}
uint32_t is_v6:1,
ignore_data_packets:1;
};
void store_payload(uint32_t seq, payload_type payload);
buffered_payload_type::iterator erase_iterator(buffered_payload_type::iterator iter);
void update_state(const TCP& tcp);
payload_type payload_;
buffered_payload_type buffered_payload_;
uint32_t seq_number_;
std::array<uint8_t, 16> dest_address_;
uint16_t dest_port_;
data_available_callback_type on_data_callback_;
out_of_order_callback_type on_out_of_order_callback_;
State state_;
int mss_;
flags flags_;
};
/**
* \brief Represents a TCP stream
*
@@ -299,16 +69,21 @@ private:
*/
class TINS_API Stream {
public:
/**
* The type used for callbacks
*/
typedef std::function<void(Stream&)> stream_callback_type;
/**
* The type used to store payloads
*/
typedef Flow::payload_type payload_type;
/**
* The type used to represent timestamps
*/
typedef std::chrono::microseconds timestamp_type;
/**
* The type used for callbacks
*/
typedef std::function<void(Stream&)> stream_callback_type;
/**
* The type used for callbacks
*
@@ -326,14 +101,30 @@ public:
/**
* \brief Constructs a TCP stream using the provided packet.
*
* \param initial_packet The first packet of the stream
* \param ts The first packet's timestamp
*/
Stream(PDU& initial_packet);
Stream(PDU& initial_packet, const timestamp_type& ts = timestamp_type());
/**
* \brief Processes this packet.
*
* This will forward the packet appropriately to the client
* or server flow.
*
* \param packet The packet to be processed
* \param ts The packet's timestamp
*/
void process_packet(PDU& packet, const timestamp_type& ts);
/**
* \brief Processes this packet.
*
* This will forward the packet appropriately to the client
* or server flow.
*
* \param packet The packet to be processed
*/
void process_packet(PDU& packet);
@@ -448,6 +239,16 @@ public:
*/
payload_type& server_payload();
/**
* Getter for the creation time of this stream
*/
const timestamp_type& create_time() const;
/**
* Getter for the last seen time of this stream
*/
const timestamp_type& last_seen() const;
/**
* \brief Sets the callback to be executed when the stream is closed
*
@@ -555,6 +356,8 @@ private:
out_of_order_callback_type on_server_out_of_order_callback_;
hwaddress_type client_hw_addr_;
hwaddress_type server_hw_addr_;
timestamp_type create_time_;
timestamp_type last_seen_;
bool auto_cleanup_;
};

View File

@@ -44,6 +44,7 @@ class PDU;
class TCP;
class IPv4Address;
class IPv6Address;
class Packet;
namespace TCPIP {
@@ -90,13 +91,20 @@ public:
* and process it, or if it belongs to a new one, in which case it
* starts tracking it.
*
* This method always returns true so it can be easily plugged as
* the argument to Sniffer::sniff_loop.
* \param packet The packet to be processed
*/
void process_packet(PDU& packet);
/**
* \brief Processes a packet
*
* This will detect if this packet belongs to an existing stream
* and process it, or if it belongs to a new one, in which case it
* starts tracking it.
*
* \param packet The packet to be processed
* \return Always true
*/
bool process_packet(PDU& packet);
void process_packet(Packet& packet);
/**
* \brief Sets the callback to be executed when a new stream is captured.
@@ -108,6 +116,17 @@ public:
*/
void new_stream_callback(const stream_callback_type& callback);
/**
* \brief Sets the maximum time a stream will be followed without capturing
* packets that belong to it.
*
* \param keep_alive The maximum time to keep unseen streams
*/
template <typename Rep, typename Period>
void stream_keep_alive(const std::chrono::duration<Rep, Period>& keep_alive) {
stream_keep_alive_ = keep_alive;
}
/**
* Finds the stream identified by the provided arguments.
*
@@ -130,8 +149,12 @@ public:
Stream& find_stream(IPv6Address client_addr, uint16_t client_port,
IPv6Address server_addr, uint16_t server_port);
private:
static const size_t DEFAULT_MAX_BUFFERED_CHUNKS;
typedef std::array<uint8_t, 16> address_type;
typedef Stream::timestamp_type timestamp_type;
static const size_t DEFAULT_MAX_BUFFERED_CHUNKS;
static const timestamp_type DEFAULT_CLEANUP_INTERVAL;
static const timestamp_type DEFAULT_KEEP_ALIVE;
struct stream_id {
stream_id(const address_type& client_addr, uint16_t client_port,
@@ -153,10 +176,14 @@ private:
Stream& find_stream(const stream_id& id);
static address_type serialize(IPv4Address address);
static address_type serialize(const IPv6Address& address);
void process_packet(PDU& packet, const timestamp_type& ts);
void cleanup_streams(const timestamp_type& now);
streams_type streams_;
stream_callback_type on_new_connection_;
size_t max_buffered_chunks_;
timestamp_type last_cleanup_;
timestamp_type stream_keep_alive_;
bool attach_to_flows_;
};