zeek/src/TCP.h
Robin Sommer af1809aaa3 First prototype of new analyzer framework.
This is a larger internal change that moves the analyzer
infrastructure to a more flexible model where the available analyzers
don't need to be hardcoded at compile time anymore. While currently
they actually still are, this will in the future enable external
analyzer plugins. For now, it does already add the capability to
dynamically enable/disable analyzers from script-land, replacing the
old Analyzer::Available() methods.

There are three major parts going into this:

    - A new plugin infrastructure in src/plugin. This is independent
      of analyzers and will eventually support plugins for other parts
      of Bro as well (think: readers and writers). The goal is that
      plugins can be alternatively compiled in statically or loadead
      dynamically at runtime from a shared library. While the latter
      isn't there yet, there'll be almost no code change for a plugin
      to make it dynamic later (hopefully :)

    - New analyzer infrastructure in src/analyzer. I've moved a number
      of analyzer-related classes here, including Analyzer and DPM;
      the latter now renamed to Analyzer::Manager. More will move here
      later. Currently, there's only one plugin here, which provides
      *all* existing analyzers. We can modularize this further in the
      future (or not).

    - A new script interface in base/framework/analyzer. I think that
      this will eventually replace the dpm framework, but for now
      that's still there as well, though some parts have moved over.

I've also remove the dpd_config table; ports are now configured via
the analyzer framework. For exmaple, for SSH:

    const ports = { 22/tcp } &redef;

    event bro_init() &priority=5
        {
        ...
        Analyzer::register_for_ports(Analyzer::ANALYZER_SSH, ports);
        }

As you can see, the old ANALYZER_SSH constants have more into an enum
in the Analyzer namespace.

This is all hardly tested right now, and not everything works yet.
There's also a lot more cleanup to do (moving more classes around;
removing no longer used functionality; documenting script and C++
interfaces; regression tests). But it seems to generally work with a
small trace at least.

The debug stream "dpm" shows more about the loaded/enabled analyzers.

A new option -N lists loaded plugins and what they provide (including
those compiled in statically; i.e., right now it outputs all the
analyzers).

This is all not cast-in-stone yet, for some things we need to see if
they make sense this way. Feedback welcome.
2013-03-26 11:05:38 -07:00

378 lines
12 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#ifndef TCP_H
#define TCP_H
#include "analyzer/Analyzer.h"
#include "TCP.h"
#include "PacketDumper.h"
#include "IPAddr.h"
#include "TCP_Endpoint.h"
#include "Conn.h"
// We define two classes here:
// - TCP_Analyzer is the analyzer for the TCP protocol itself.
// - TCP_ApplicationAnalyzer is an abstract base class for analyzers for a
// protocol running on top of TCP.
class PIA_TCP;
class TCP_ApplicationAnalyzer;
class TCP_Reassembler;
class TCP_Endpoint;
class TCP_Flags {
public:
TCP_Flags(const struct tcphdr* tp) { flags = tp->th_flags; }
bool SYN() { return flags & TH_SYN; }
bool FIN() { return flags & TH_FIN; }
bool RST() { return flags & TH_RST; }
bool ACK() { return flags & TH_ACK; }
bool URG() { return flags & TH_URG; }
bool PUSH() { return flags & TH_PUSH; }
protected:
u_char flags;
};
class TCP_Analyzer : public analyzer::TransportLayerAnalyzer {
public:
TCP_Analyzer(Connection* conn);
virtual ~TCP_Analyzer();
void EnableReassembly();
// Add a child analyzer that will always get the packets,
// independently of whether we do any reassembly.
void AddChildPacketAnalyzer(analyzer::Analyzer* a)
{ packet_children.push_back(a); a->SetParent(this); }
// True if the connection has closed in some sense, false otherwise.
int IsClosed() const { return orig->did_close || resp->did_close; }
int BothClosed() const { return orig->did_close && resp->did_close; }
int IsPartial() const { return is_partial; }
bool HadGap(bool orig) const;
TCP_Endpoint* Orig() const { return orig; }
TCP_Endpoint* Resp() const { return resp; }
int OrigState() const { return orig->state; }
int RespState() const { return resp->state; }
int OrigPrevState() const { return orig->prev_state; }
int RespPrevState() const { return resp->prev_state; }
uint32 OrigSeq() const { return orig->LastSeq(); }
uint32 RespSeq() const { return resp->LastSeq(); }
// True if either endpoint still has pending data. closing_endp
// is an endpoint that has indicated it is closing (i.e., for
// which we have seen a FIN) - for it, data is pending unless
// everything's been delivered up to the FIN. For its peer,
// the test is whether it has any outstanding, un-acked data.
int DataPending(TCP_Endpoint* closing_endp);
virtual void SetContentsFile(unsigned int direction, BroFile* f);
virtual BroFile* GetContentsFile(unsigned int direction) const;
// Callback to process a TCP option.
typedef int (*proc_tcp_option_t)(unsigned int opt, unsigned int optlen,
const u_char* option, TCP_Analyzer* analyzer,
bool is_orig, void* cookie);
// From Analyzer.h
virtual void UpdateConnVal(RecordVal *conn_val);
// Needs to be static because it's passed as a pointer-to-function
// rather than pointer-to-member-function.
static int ParseTCPOptions(const struct tcphdr* tcp,
proc_tcp_option_t proc, TCP_Analyzer* analyzer,
bool is_orig, void* cookie);
static analyzer::Analyzer* InstantiateAnalyzer(Connection* conn)
{ return new TCP_Analyzer(conn); }
static bool Available() { return true; }
protected:
friend class TCP_ApplicationAnalyzer;
friend class TCP_Reassembler;
friend class PIA_TCP;
// Analyzer interface.
virtual void Init();
virtual void Done();
virtual void DeliverPacket(int len, const u_char* data, bool orig, int seq, const IP_Hdr* ip, int caplen);
virtual void DeliverStream(int len, const u_char* data, bool orig);
virtual void Undelivered(int seq, int len, bool orig);
virtual void FlipRoles();
virtual bool IsReuse(double t, const u_char* pkt);
// Returns the TCP header pointed to by data (which we assume is
// aligned), updating data, len & caplen. Returns nil if the header
// isn't fully present.
const struct tcphdr* ExtractTCP_Header(const u_char*& data, int& len,
int& caplen);
// Returns true if the checksum is valid, false if not (and in which
// case also updates the status history of the endpoint).
bool ValidateChecksum(const struct tcphdr* tp, TCP_Endpoint* endpoint,
int len, int caplen);
// Update analysis based on flag combinations. The endpoint, base_seq
// and len are needed for tracking various history information.
// dst_port is needed for trimming of FIN packets.
void CheckFlagCombos(TCP_Flags flags, TCP_Endpoint* endpoint,
uint32 base_seq, int len, int dst_port);
void UpdateWindow(TCP_Endpoint* endpoint, unsigned int window,
uint32 base_seq, uint32 ack_seq,
TCP_Flags flags);
void ProcessSYN(const IP_Hdr* ip, const struct tcphdr* tp,
uint32 tcp_hdr_len, int& seq_len,
TCP_Endpoint* endpoint, TCP_Endpoint* peer,
uint32 base_seq, uint32 ack_seq,
const IPAddr& orig_addr,
int is_orig, TCP_Flags flags);
void ProcessFIN(double t, TCP_Endpoint* endpoint, int& seq_len,
uint32 base_seq);
void ProcessRST(double t, TCP_Endpoint* endpoint, const IP_Hdr* ip,
uint32 base_seq, int len, int& seq_len);
void ProcessACK(TCP_Endpoint* endpoint, TCP_Endpoint* peer,
uint32 ack_seq, int is_orig, TCP_Flags flags);
void ProcessFlags(double t, const IP_Hdr* ip, const struct tcphdr* tp,
uint32 tcp_hdr_len, int len, int& seq_len,
TCP_Endpoint* endpoint, TCP_Endpoint* peer,
uint32 base_seq, uint32 ack_seq,
const IPAddr& orig_addr,
int is_orig, TCP_Flags flags);
void TransitionFromInactive(double t, TCP_Endpoint* endpoint,
uint32 base_seq, uint32 last_seq,
int SYN);
// Update the state machine of the TCPs based on the activity. This
// includes our pseudo-states such as TCP_ENDPOINT_PARTIAL.
//
// On return, do_close is true if we should consider the connection
// as closed, and gen_event if we shouuld generate an event about
// this fact.
void UpdateStateMachine(double t,
TCP_Endpoint* endpoint, TCP_Endpoint* peer,
uint32 base_seq, uint32 ack_seq, uint32 last_seq,
int len, int delta_last, int is_orig, TCP_Flags flags,
int& do_close, int& gen_event);
void UpdateInactiveState(double t,
TCP_Endpoint* endpoint, TCP_Endpoint* peer,
uint32 base_seq, uint32 ack_seq,
int len, int is_orig, TCP_Flags flags,
int& do_close, int& gen_event);
void UpdateSYN_SentState(double t,
TCP_Endpoint* endpoint, TCP_Endpoint* peer,
uint32 base_seq, uint32 last_seq,
int len, int is_orig, TCP_Flags flags,
int& do_close, int& gen_event);
void UpdateEstablishedState(double t,
TCP_Endpoint* endpoint, TCP_Endpoint* peer,
uint32 base_seq, uint32 last_seq,
int is_orig, TCP_Flags flags,
int& do_close, int& gen_event);
void UpdateClosedState(double t, TCP_Endpoint* endpoint,
int delta_last, TCP_Flags flags,
int& do_close);
void UpdateResetState(int len, TCP_Flags flags, TCP_Endpoint* endpoint,
uint32 base_seq, uint32 last_seq);
void GeneratePacketEvent(TCP_Endpoint* endpoint, TCP_Endpoint* peer,
uint32 base_seq, uint32 ack_seq,
const u_char* data, int len, int caplen,
int is_orig, TCP_Flags flags);
int DeliverData(double t, const u_char* data, int len, int caplen,
const IP_Hdr* ip, const struct tcphdr* tp,
TCP_Endpoint* endpoint, uint32 base_seq,
int is_orig, TCP_Flags flags);
void CheckRecording(int need_contents, TCP_Flags flags);
void CheckPIA_FirstPacket(int is_orig, const IP_Hdr* ip);
// Returns the difference between last_seq and the last sequence
// seen by the endpoint (may be negative).
int UpdateLastSeq(TCP_Endpoint* endpoint, uint32 last_seq,
TCP_Flags flags);
friend class ConnectionTimer;
void AttemptTimer(double t);
void PartialCloseTimer(double t);
void ExpireTimer(double t);
void ResetTimer(double t);
void DeleteTimer(double t);
void ConnDeleteTimer(double t);
void EndpointEOF(TCP_Reassembler* endp);
void ConnectionClosed(TCP_Endpoint* endpoint,
TCP_Endpoint* peer, int gen_event);
void ConnectionFinished(int half_finished);
void ConnectionReset();
void PacketWithRST();
void SetReassembler(TCP_Reassembler* rorig, TCP_Reassembler* rresp);
Val* BuildSYNPacketVal(int is_orig,
const IP_Hdr* ip, const struct tcphdr* tcp);
RecordVal* BuildOSVal(int is_orig, const IP_Hdr* ip,
const struct tcphdr* tcp, uint32 tcp_hdr_len);
// Needs to be static because it's passed as a pointer-to-function
// rather than pointer-to-member-function.
static int TCPOptionEvent(unsigned int opt, unsigned int optlen,
const u_char* option, TCP_Analyzer* analyzer,
bool is_orig, void* cookie);
private:
TCP_Endpoint* orig;
TCP_Endpoint* resp;
typedef list<analyzer::Analyzer*> analyzer_list;
analyzer_list packet_children;
unsigned int first_packet_seen: 2;
unsigned int reassembling: 1;
unsigned int is_partial: 1;
unsigned int is_active: 1;
unsigned int finished: 1;
// Whether we're waiting on final data delivery before closing
// this connection.
unsigned int close_deferred: 1;
// Whether to generate an event when we finally do close it.
unsigned int deferred_gen_event: 1;
// Whether we have seen the first ACK from the originator.
unsigned int seen_first_ACK: 1;
};
class TCP_ApplicationAnalyzer : public analyzer::Analyzer {
public:
TCP_ApplicationAnalyzer(const char* name, Connection* conn)
: Analyzer(name, conn)
{ tcp = 0; }
virtual ~TCP_ApplicationAnalyzer() { }
// This may be nil if we are not directly associated with a TCP
// analyzer (e.g., we're part of a tunnel decapsulation pipeline).
TCP_Analyzer* TCP()
{
return tcp ?
tcp :
static_cast<TCP_Analyzer*>(Conn()->FindAnalyzer("TCP"));
}
void SetTCP(TCP_Analyzer* arg_tcp) { tcp = arg_tcp; }
// The given endpoint's data delivery is complete.
virtual void EndpointEOF(bool is_orig);
// Called whenever an end enters TCP_ENDPOINT_CLOSED or
// TCP_ENDPOINT_RESET. If gen_event is true and the connection
// is now fully closed, a connection_finished event will be
// generated; otherwise not.
virtual void ConnectionClosed(TCP_Endpoint* endpoint,
TCP_Endpoint* peer, int gen_event);
virtual void ConnectionFinished(int half_finished);
virtual void ConnectionReset();
// Called whenever a RST packet is seen - sometimes the invocation
// of ConnectionReset is delayed.
virtual void PacketWithRST();
virtual void DeliverPacket(int len, const u_char* data, bool orig,
int seq, const IP_Hdr* ip, int caplen);
virtual void Init();
// This suppresses violations if the TCP connection wasn't
// fully established.
virtual void ProtocolViolation(const char* reason,
const char* data = 0, int len = 0);
// "name" and "val" both now belong to this object, which needs to
// delete them when done with them.
virtual void SetEnv(bool orig, char* name, char* val);
private:
TCP_Analyzer* tcp;
};
class TCP_SupportAnalyzer : public analyzer::SupportAnalyzer {
public:
TCP_SupportAnalyzer(const char* name, Connection* conn, bool arg_orig)
: analyzer::SupportAnalyzer(name, conn, arg_orig) { }
virtual ~TCP_SupportAnalyzer() {}
// These are passed on from TCP_Analyzer.
virtual void EndpointEOF(bool is_orig) { }
virtual void ConnectionClosed(TCP_Endpoint* endpoint,
TCP_Endpoint* peer, int gen_event) { }
virtual void ConnectionFinished(int half_finished) { }
virtual void ConnectionReset() { }
virtual void PacketWithRST() { }
};
class TCPStats_Endpoint {
public:
TCPStats_Endpoint(TCP_Endpoint* endp);
int DataSent(double t, int seq, int len, int caplen, const u_char* data,
const IP_Hdr* ip, const struct tcphdr* tp);
RecordVal* BuildStats();
protected:
TCP_Endpoint* endp;
int num_pkts;
int num_rxmit;
int num_rxmit_bytes;
int num_in_order;
int num_OO;
int num_repl;
int max_top_seq;
int last_id;
int endian_type;
};
class TCPStats_Analyzer : public TCP_ApplicationAnalyzer {
public:
TCPStats_Analyzer(Connection* c);
~TCPStats_Analyzer();
virtual void Init();
virtual void Done();
static analyzer::Analyzer* InstantiateAnalyzer(Connection* conn)
{ return new TCPStats_Analyzer(conn); }
static bool Available() { return conn_stats || tcp_rexmit; }
protected:
virtual void DeliverPacket(int len, const u_char* data, bool is_orig,
int seq, const IP_Hdr* ip, int caplen);
TCPStats_Endpoint* orig_stats;
TCPStats_Endpoint* resp_stats;
};
#endif