From: Russ Combs (rucombs) Date: Fri, 2 Oct 2015 17:11:43 +0000 (-0400) Subject: Merge pull request #34 in SNORT/snort3 from stream_dev to master X-Git-Tag: 3.0.0-233~806 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=77447e14950ff40febb3136295b216aa3de60a59;p=thirdparty%2Fsnort3.git Merge pull request #34 in SNORT/snort3 from stream_dev to master Squashed commit of the following: commit 0029a412c7434f5a32c8ebf06e688695241057e2 Merge: 1d6c94b e662907 Author: davis mcpherson Date: Wed Sep 23 17:05:47 2015 +0000 Merge branch 'stream_dev' of https://stash-eng-rtp1.cisco.com/stash/scm/snort/snort3 into stream_dev commit 1d6c94baecd2f800144c46d90aa54662502ba1ce Author: davis mcpherson Date: Wed Sep 23 12:55:01 2015 -0400 code cleanup from review comments, add c/s trackers, session to tcp norm class, use tcp flags helper functions commit b2b73179b98333fd22d9cd9f2dfdc5da718b8d71 Author: davis mcpherson Date: Tue Sep 22 09:22:30 2015 -0400 fix merge issue after rebase with master commit 6a18d85328ea868fddccab5e9a2804a35808e4dc Author: davis mcpherson Date: Tue Sep 15 12:50:24 2015 -0400 merge of latest tcp normalization refactoring commit e662907c2639d9d2459158624538da7fc03565e6 Author: davis mcpherson Date: Wed Sep 23 12:55:01 2015 -0400 code cleanup from review comments, add c/s trackers, session to tcp norm class, use tcp flags helper functions commit 2c349a9753993ce2d60cb119ac0d8e29a6d10d8e Merge: b97bed9 3a88b13 Author: davis mcpherson Date: Tue Sep 22 10:14:51 2015 -0400 fix merge issue pulling latest from origin commit b97bed97696dc55b79961c3b154594bfb78fe029 Author: davis mcpherson Date: Tue Sep 22 09:22:30 2015 -0400 fix merge issue after rebase with master commit 797672c0dd77a424b4fa0480ce30767726a6cac0 Author: davis mcpherson Date: Tue Sep 15 12:50:24 2015 -0400 merge of latest tcp normalization refactoring commit 3a88b134a557fc33cd30095588dd82d6c1d58fa1 Author: davis mcpherson Date: Tue Sep 15 12:50:24 2015 -0400 merge of latest tcp normalization refactoring --- diff --git a/src/flow/flow.h b/src/flow/flow.h index f23140bda..9ef6b1d1c 100644 --- a/src/flow/flow.h +++ b/src/flow/flow.h @@ -156,6 +156,7 @@ public: void call_handlers(Packet* p, bool eof = false); void markup_packet_flags(Packet*); + void set_direction(Packet*); void set_expire(const Packet*, uint32_t timeout); @@ -164,6 +165,39 @@ public: void set_ttl(Packet*, bool client); + uint32_t update_session_flags( uint32_t flags ) + { + return ssn_state.session_flags = flags; + } + + uint32_t set_session_flags(uint32_t flags) + { + return ssn_state.session_flags |= flags; + } + + uint32_t clear_session_flags(uint32_t flags) + { + return ssn_state.session_flags &= ~flags; + } + + uint32_t get_session_flags(void) + { + return ssn_state.session_flags; + } + + int get_ignore_direction(void) + { + return ssn_state.ignore_direction; + } + + int set_ignore_direction(char ignore_direction) + { + if (ssn_state.ignore_direction != ignore_direction) + ssn_state.ignore_direction = ignore_direction; + + return ssn_state.ignore_direction; + } + bool two_way_traffic() { return (ssn_state.session_flags & SSNFLAG_SEEN_BOTH) == SSNFLAG_SEEN_BOTH; } @@ -260,7 +294,6 @@ public: // FIXIT-M privatize if possible unsigned policy_id; FlowState flow_state; - LwState ssn_state; // FIXIT-L can client and server ip and port be removed from flow? sfip_t client_ip; // FIXIT-L family and bits should be changed to uint16_t @@ -281,6 +314,10 @@ public: // FIXIT-M privatize if possible uint8_t outer_client_ttl, outer_server_ttl; uint8_t response_count; + +public: + LwState ssn_state; + }; #endif diff --git a/src/main/snort.cc b/src/main/snort.cc index 801ea7236..23320d53f 100644 --- a/src/main/snort.cc +++ b/src/main/snort.cc @@ -100,6 +100,7 @@ using namespace std; #include "control/idle_processing.h" #include "file_api/file_service.h" #include "flow/flow_control.h" +#include "flow/flow.h" #include "stream/stream.h" #include "target_based/sftarget_reader.h" @@ -784,7 +785,7 @@ static DAQ_Verdict update_verdict(DAQ_Verdict verdict, int& inject) } } else if ( (s_packet->packet_flags & PKT_IGNORE) || - (stream.get_ignore_direction(s_packet->flow) == SSN_DIR_BOTH) ) + (s_packet->flow && s_packet->flow->get_ignore_direction( ) == SSN_DIR_BOTH) ) { if ( !Active::get_tunnel_bypass() ) { @@ -798,7 +799,8 @@ static DAQ_Verdict update_verdict(DAQ_Verdict verdict, int& inject) } else if ( s_packet->ptrs.decode_flags & DECODE_PKT_TRUST ) { - stream.set_ignore_direction(s_packet->flow, SSN_DIR_BOTH); + if(s_packet->flow) + s_packet->flow->set_ignore_direction(SSN_DIR_BOTH); verdict = DAQ_VERDICT_WHITELIST; } else diff --git a/src/network_inspectors/normalize/norm_module.cc b/src/network_inspectors/normalize/norm_module.cc index b01cd2e5a..372b50488 100644 --- a/src/network_inspectors/normalize/norm_module.cc +++ b/src/network_inspectors/normalize/norm_module.cc @@ -25,6 +25,7 @@ #include #include "stream/stream.h" +#include "stream/tcp/tcp_normalizer.h" using namespace std; @@ -394,7 +395,7 @@ const PegInfo* NormalizeModule::get_pegs() const p++; } - p = Stream_GetNormPegs(); + p = TcpNormalizer::get_normalization_pegs(); assert(p); while ( p->name ) @@ -422,7 +423,7 @@ PegCount* NormalizeModule::get_counts() const counts.push_back(p[i][NORM_MODE_TEST]); } - p = Stream_GetNormCounts(c); + p = TcpNormalizer::get_normalization_counts(c); for ( unsigned i = 0; i < c; ++i ) { diff --git a/src/network_inspectors/port_scan/ps_detect.cc b/src/network_inspectors/port_scan/ps_detect.cc index f2a4b49bc..5ceb1f8c5 100644 --- a/src/network_inspectors/port_scan/ps_detect.cc +++ b/src/network_inspectors/port_scan/ps_detect.cc @@ -784,7 +784,7 @@ int PortScan::ps_tracker_update_tcp(PS_PKT* ps_pkt, PS_TRACKER* scanner, ** Otherwise, only consider streams not picked up midstream. */ if ( p->flow ) - session_flags = stream.get_session_flags(p->flow); + session_flags = p->flow->get_session_flags(); if ( session_flags & (SSNFLAG_SEEN_CLIENT|SSNFLAG_SEEN_SERVER) ) { diff --git a/src/protocols/tcp.h b/src/protocols/tcp.h index 59ff84ca6..61e751ee5 100644 --- a/src/protocols/tcp.h +++ b/src/protocols/tcp.h @@ -71,14 +71,14 @@ struct TCPHdr uint16_t th_sport; /* source port */ uint16_t th_dport; /* destination port */ uint32_t th_seq; /* sequence number */ - uint32_t th_ack; /* acknowledgement number */ + uint32_t th_ack; /* acknowledgment number */ uint8_t th_offx2; /* offset and reserved */ uint8_t th_flags; uint16_t th_win; /* window */ uint16_t th_sum; /* checksum */ uint16_t th_urp; /* urgent pointer */ - /* Fomatted data access and booleans */ + /* Formatted data access and booleans */ inline uint8_t hlen() const { return (th_offx2 & 0xf0) >> 2; } @@ -119,10 +119,10 @@ struct TCPHdr { return (th_flags & TH_SYN); } inline bool is_syn_only() const - { return (th_flags & (TH_SYN|TH_ACK)) == TH_SYN; } + { return (th_flags & (TH_SYN | TH_ACK | TH_RST | TH_FIN)) == TH_SYN; } inline bool is_syn_ack() const - { return are_flags_set(TH_SYN|TH_ACK); } + { return are_flags_set(TH_SYN | TH_ACK); } inline bool is_ack() const { return (th_flags & TH_ACK); } diff --git a/src/service_inspectors/dns/dns.cc b/src/service_inspectors/dns/dns.cc index 07bef715f..0edc3dc1d 100644 --- a/src/service_inspectors/dns/dns.cc +++ b/src/service_inspectors/dns/dns.cc @@ -1005,7 +1005,7 @@ static void snort_dns(Packet* p) /* If session picked up mid-stream, do not process further. * Would be almost impossible to tell where we are in the * data stream. */ - if ( stream.get_session_flags(p->flow) & SSNFLAG_MIDSTREAM ) + if ( p->flow->get_session_flags() & SSNFLAG_MIDSTREAM ) { return; } @@ -1085,6 +1085,7 @@ void Dns::eval(Packet* p) { // precondition - what we registered for assert((p->is_udp() and p->dsize and p->data) or p->has_tcp_data()); + assert(p->flow); ++dnsstats.total_packets; snort_dns(p); diff --git a/src/service_inspectors/ftp_telnet/ftp_data.cc b/src/service_inspectors/ftp_telnet/ftp_data.cc index f1cf5e47b..82ca6eb3f 100644 --- a/src/service_inspectors/ftp_telnet/ftp_data.cc +++ b/src/service_inspectors/ftp_telnet/ftp_data.cc @@ -91,7 +91,7 @@ static void FTPDataProcess( * and preprocessor was configured to ignore ftp-data sessions. */ if (!status && data_ssn->data_chan) { - stream.set_ignore_direction(p->flow, SSN_DIR_BOTH); + p->flow->set_ignore_direction(SSN_DIR_BOTH); } } @@ -130,7 +130,8 @@ static int SnortFTPData(Packet* p) "FTP-DATA Invalid FTP_SESSION retrieved durring lookup\n"); if (data_ssn->data_chan) - stream.set_ignore_direction(p->flow, SSN_DIR_BOTH); + p->flow->set_ignore_direction(SSN_DIR_BOTH); + return -2; } @@ -144,7 +145,7 @@ static int SnortFTPData(Packet* p) case FTPP_FILE_IGNORE: /* This wasn't a file transfer; ignore it */ if (data_ssn->data_chan) - stream.set_ignore_direction(p->flow, SSN_DIR_BOTH); + p->flow->set_ignore_direction(SSN_DIR_BOTH); return 0; default: diff --git a/src/service_inspectors/imap/imap.cc b/src/service_inspectors/imap/imap.cc index 971ed6e25..7c27b07a1 100644 --- a/src/service_inspectors/imap/imap.cc +++ b/src/service_inspectors/imap/imap.cc @@ -634,7 +634,7 @@ static void snort_imap(IMAP_PROTO_CONF* config, Packet* p) { imap_ssn->state = STATE_TLS_DATA; } - else if (!(stream.get_session_flags(p->flow) & SSNFLAG_MIDSTREAM) + else if (!(p->flow->get_session_flags() & SSNFLAG_MIDSTREAM) && !stream.missed_packets(p->flow, SSN_DIR_BOTH)) { /* revert back to command state - assume server didn't accept STARTTLS */ @@ -764,6 +764,7 @@ void Imap::eval(Packet* p) PROFILE_VARS; // precondition - what we registered for assert(p->has_tcp_data()); + assert(p->flow); ++imapstats.total_packets; diff --git a/src/service_inspectors/pop/pop.cc b/src/service_inspectors/pop/pop.cc index 3de4a4130..88f97b555 100644 --- a/src/service_inspectors/pop/pop.cc +++ b/src/service_inspectors/pop/pop.cc @@ -569,7 +569,7 @@ static void snort_pop(POP_PROTO_CONF* config, Packet* p) { pop_ssn->state = STATE_TLS_DATA; } - else if (!(stream.get_session_flags(p->flow) & SSNFLAG_MIDSTREAM) + else if (!(p->flow->get_session_flags() & SSNFLAG_MIDSTREAM) && !stream.missed_packets(p->flow, SSN_DIR_BOTH)) { /* revert back to command state - assume server didn't accept STARTTLS */ @@ -699,6 +699,7 @@ void Pop::eval(Packet* p) PROFILE_VARS; // precondition - what we registered for assert(p->has_tcp_data()); + assert(p->flow); ++popstats.total_packets; diff --git a/src/service_inspectors/sip/sip.cc b/src/service_inspectors/sip/sip.cc index 25732168f..7884852b7 100644 --- a/src/service_inspectors/sip/sip.cc +++ b/src/service_inspectors/sip/sip.cc @@ -335,6 +335,7 @@ void Sip::eval(Packet* p) { // precondition - what we registered for assert((p->is_udp() and p->dsize and p->data) or p->has_tcp_data()); + assert(p->flow); ++sipstats.total_packets; snort_sip(config, p); diff --git a/src/service_inspectors/sip/sip_dialog.cc b/src/service_inspectors/sip/sip_dialog.cc index 624c9e56f..fdb2338c0 100644 --- a/src/service_inspectors/sip/sip_dialog.cc +++ b/src/service_inspectors/sip/sip_dialog.cc @@ -425,7 +425,7 @@ static int SIP_ignoreChannels(SIP_DialogData* dialog, Packet* p, SIP_PROTO_CONF* #endif if ( fd ) { - stream.set_ignore_direction(p->flow, SSN_DIR_BOTH); + p->flow->set_ignore_direction(SSN_DIR_BOTH); } else { diff --git a/src/service_inspectors/smtp/smtp.cc b/src/service_inspectors/smtp/smtp.cc index b175f5d78..1afe97308 100644 --- a/src/service_inspectors/smtp/smtp.cc +++ b/src/service_inspectors/smtp/smtp.cc @@ -1076,7 +1076,7 @@ static void SMTP_ProcessServerPacket(SMTP_PROTO_CONF* config, Packet* p, SMTPDat { smtp_ssn->state = STATE_TLS_DATA; } - else if (!(stream.get_session_flags(p->flow) & SSNFLAG_MIDSTREAM) + else if (!(p->flow->get_session_flags() & SSNFLAG_MIDSTREAM) && !stream.missed_packets(p->flow, SSN_DIR_BOTH)) { /* Check to see if the raw packet is in order */ @@ -1527,6 +1527,7 @@ void Smtp::eval(Packet* p) PROFILE_VARS; // precondition - what we registered for assert(p->has_tcp_data()); + assert(p->flow); ++smtpstats.total_packets; diff --git a/src/service_inspectors/ssh/ssh.cc b/src/service_inspectors/ssh/ssh.cc index 2990eb7ee..cf1df7573 100644 --- a/src/service_inspectors/ssh/ssh.cc +++ b/src/service_inspectors/ssh/ssh.cc @@ -186,7 +186,7 @@ static void snort_ssh(SSH_PROTO_CONF* config, Packet* p) /* If we picked up mid-stream or missed any packets (midstream pick up * * means we've already missed packets) set missed packets flag and make * * sure we don't do any more reassembly on this session */ - if ((stream.get_session_flags(p->flow) & SSNFLAG_MIDSTREAM) + if ((p->flow->get_session_flags() & SSNFLAG_MIDSTREAM) || stream.missed_packets(p->flow, SSN_DIR_BOTH)) { /* Order only matters if the packets are not encrypted */ @@ -809,6 +809,7 @@ void Ssh::eval(Packet* p) { // precondition - what we registered for assert(p->has_tcp_data()); + assert(p->flow); ++sshstats.total_packets; snort_ssh(config, p); diff --git a/src/service_inspectors/ssl/ssl_inspector.cc b/src/service_inspectors/ssl/ssl_inspector.cc index d4a357295..bafc090f0 100644 --- a/src/service_inspectors/ssl/ssl_inspector.cc +++ b/src/service_inspectors/ssl/ssl_inspector.cc @@ -144,7 +144,7 @@ static inline bool SSLPP_is_encrypted(SSL_PROTO_CONF* config, uint32_t ssl_flags } /* Check if we're either midstream or if packets were missed after the * * connection was established */ - else if ((stream.get_session_flags (packet->flow) & SSNFLAG_MIDSTREAM) || + else if ((packet->flow->get_session_flags() & SSNFLAG_MIDSTREAM) || (stream.missed_packets(packet->flow, SSN_DIR_BOTH))) { if ((ssl_flags & (SSL_CAPP_FLAG | SSL_SAPP_FLAG)) == (SSL_CAPP_FLAG | SSL_SAPP_FLAG)) @@ -444,6 +444,7 @@ void Ssl::eval(Packet* p) { // precondition - what we registered for assert(p->has_tcp_data()); + assert(p->flow); ++sslstats.total_packets; snort_ssl(config, p); diff --git a/src/stream/stream_api.cc b/src/stream/stream_api.cc index e614ac781..247f983bd 100644 --- a/src/stream/stream_api.cc +++ b/src/stream/stream_api.cc @@ -331,47 +331,6 @@ void Stream::drop_session(const Packet* p) drop_traffic(flow, SSN_DIR_BOTH); } -uint32_t Stream::set_session_flags(Flow* flow, uint32_t flags) -{ - if ( !flow ) - return 0; - - if ((flow->ssn_state.session_flags & flags) != flags) - { - flow->ssn_state.session_flags |= flags; - } - return flow->ssn_state.session_flags; -} - -uint32_t Stream::get_session_flags(Flow* flow) -{ - if ( !flow ) - return 0; - - return flow->ssn_state.session_flags; -} - -int Stream::get_ignore_direction(Flow* flow) -{ - if ( !flow ) - return 0; - - return flow->ssn_state.ignore_direction; -} - -int Stream::set_ignore_direction(Flow* flow, int ignore_direction) -{ - if ( !flow ) - return 0; - - if (flow->ssn_state.ignore_direction != ignore_direction) - { - flow->ssn_state.ignore_direction = ignore_direction; - } - - return flow->ssn_state.ignore_direction; -} - //------------------------------------------------------------------------- // misc support //------------------------------------------------------------------------- @@ -853,24 +812,24 @@ TEST_CASE("Stream API", "[stream_api][stream]") SECTION("set/get ignore direction") { - int dir = Stream::set_ignore_direction( flow, SSN_DIR_NONE); + int dir = flow->set_ignore_direction( SSN_DIR_NONE); CHECK( ( dir == SSN_DIR_NONE ) ); - dir = Stream::get_ignore_direction( flow ); + dir = flow->get_ignore_direction( ); CHECK( ( dir == SSN_DIR_NONE ) ); - dir = Stream::set_ignore_direction( flow, SSN_DIR_FROM_CLIENT); + dir = flow->set_ignore_direction( SSN_DIR_FROM_CLIENT); CHECK( ( dir == SSN_DIR_FROM_CLIENT ) ); - dir = Stream::get_ignore_direction( flow ); + dir = flow->get_ignore_direction( ); CHECK( ( dir == SSN_DIR_FROM_CLIENT ) ); - dir = Stream::set_ignore_direction( flow, SSN_DIR_FROM_SERVER); + dir = flow->set_ignore_direction( SSN_DIR_FROM_SERVER); CHECK( ( dir == SSN_DIR_FROM_SERVER ) ); - dir = Stream::get_ignore_direction( flow ); + dir = flow->get_ignore_direction( ); CHECK( ( dir == SSN_DIR_FROM_SERVER ) ); - dir = Stream::set_ignore_direction( flow, SSN_DIR_BOTH); + dir = flow->set_ignore_direction( SSN_DIR_BOTH); CHECK( ( dir == SSN_DIR_BOTH ) ); - dir = Stream::get_ignore_direction( flow ); + dir = flow->get_ignore_direction( ); CHECK( ( dir == SSN_DIR_BOTH ) ); } @@ -881,12 +840,12 @@ TEST_CASE("Stream API", "[stream_api][stream]") int dir; Stream::stop_inspection( flow, pkt, SSN_DIR_FROM_CLIENT, 0, 0 ); - dir = Stream::get_ignore_direction( flow ); + dir = flow->get_ignore_direction( ); CHECK( ( dir == SSN_DIR_FROM_CLIENT ) ); CHECK( ( flow->flow_state == Flow::ALLOW ) ); Stream::stop_inspection( flow, pkt, SSN_DIR_FROM_SERVER, 0, 0 ); - dir = Stream::get_ignore_direction( flow ); + dir = flow->get_ignore_direction( ); CHECK( ( dir == SSN_DIR_FROM_SERVER ) ); CHECK( ( flow->flow_state == Flow::ALLOW ) ); diff --git a/src/stream/stream_api.h b/src/stream/stream_api.h index 750b9a5b4..ddf78a5fc 100644 --- a/src/stream/stream_api.h +++ b/src/stream/stream_api.h @@ -241,15 +241,9 @@ public: static void set_application_protocol_id_from_host_entry( Flow*, const struct HostAttributeEntry*, int direction); - static uint32_t set_session_flags(Flow*, uint32_t flags); - static uint32_t get_session_flags(Flow*); - static bool is_midstream(Flow* flow) { return flow->ssn_state.session_flags & SSNFLAG_MIDSTREAM; } - static int get_ignore_direction(Flow*); - static int set_ignore_direction(Flow*, int ignore_direction); - // Get the TTL value used at session setup // Set outer=false to get inner ip ttl for ip in ip; else outer=true static uint8_t get_session_ttl(Flow*, char dir, bool outer); diff --git a/src/stream/tcp/CMakeLists.txt b/src/stream/tcp/CMakeLists.txt index fd91a6aae..aa9146e32 100644 --- a/src/stream/tcp/CMakeLists.txt +++ b/src/stream/tcp/CMakeLists.txt @@ -6,8 +6,10 @@ add_library( stream_tcp STATIC tcp_module.h tcp_defs.h tcp_events.h - tcp_normalization.h - tcp_normalization.cc + tcp_normalizer.h + tcp_normalizer.cc + tcp_normalizers.h + tcp_normalizers.cc tcp_reassembly.h tcp_reassembly.cc tcp_session.cc diff --git a/src/stream/tcp/Makefile.am b/src/stream/tcp/Makefile.am index 18f2121ad..5845df441 100644 --- a/src/stream/tcp/Makefile.am +++ b/src/stream/tcp/Makefile.am @@ -9,8 +9,10 @@ tcp_module.h \ tcp_debug_trace.h \ tcp_defs.h \ tcp_events.h \ -tcp_normalization.h \ -tcp_normalization.cc \ +tcp_normalizer.h \ +tcp_normalizer.cc \ +tcp_normalizers.h \ +tcp_normalizers.cc \ tcp_reassembly.h \ tcp_reassembly.cc \ tcp_session.cc \ diff --git a/src/stream/tcp/tcp_defs.h b/src/stream/tcp/tcp_defs.h index 49d3f7650..23270e02a 100644 --- a/src/stream/tcp/tcp_defs.h +++ b/src/stream/tcp/tcp_defs.h @@ -23,6 +23,7 @@ #define TCP_DEFS_H #include "main/snort_debug.h" +#include "protocols/packet.h" /* TCP states */ #define TCP_STATE_NONE 0 @@ -95,8 +96,27 @@ #define REASSEMBLY_POLICY_VISTA 13 #define REASSEMBLY_POLICY_DEFAULT REASSEMBLY_POLICY_BSD +/* target-based policy types */ +// changes to this enum require changes to stream_api.h::TCP_POLICIES +#define STREAM_POLICY_FIRST 1 +#define STREAM_POLICY_LAST 2 +#define STREAM_POLICY_LINUX 3 +#define STREAM_POLICY_OLD_LINUX 4 +#define STREAM_POLICY_BSD 5 +#define STREAM_POLICY_MACOS 6 +#define STREAM_POLICY_SOLARIS 7 +#define STREAM_POLICY_IRIX 8 +#define STREAM_POLICY_HPUX11 9 +#define STREAM_POLICY_HPUX10 10 +#define STREAM_POLICY_WINDOWS 11 +#define STREAM_POLICY_WINDOWS2K3 12 +#define STREAM_POLICY_VISTA 13 +#define STREAM_POLICY_PROXY 14 +#define STREAM_POLICY_DEFAULT STREAM_POLICY_BSD + struct TcpDataBlock { + Packet* pkt; uint32_t seq; uint32_t ack; uint32_t win; diff --git a/src/stream/tcp/tcp_normalization.cc b/src/stream/tcp/tcp_normalization.cc deleted file mode 100644 index 33649e3f7..000000000 --- a/src/stream/tcp/tcp_normalization.cc +++ /dev/null @@ -1,407 +0,0 @@ -//-------------------------------------------------------------------------- -// Copyright (C) 2014-2015 Cisco and/or its affiliates. All rights reserved. -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License Version 2 as published -// by the Free Software Foundation. You may not use, modify or distribute -// this program under any other version of the GNU General Public License. -// -// 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 -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -//-------------------------------------------------------------------------- - -// tcp_normalization.cc author davis mcpherson -// Created on: Jul 31, 2015 - -#include "tcp_normalization.h" -#include "tcp_events.h" - -THREAD_LOCAL PegCount normStats[PC_MAX][NORM_MODE_MAX]; - -static const PegInfo pegName[] = -{ { "tcp trim syn", "tcp segments trimmed on SYN" }, - { "tcp trim rst", "RST packets with data trimmed" }, - { "tcp trim win", "data trimed to window" }, - { "tcp trim mss", "data trimmed to MSS" }, - { "tcp ecn session", "ECN bits cleared" }, - { "tcp ts nop", "timestamp options cleared" }, - { "tcp ips data", "normalized segments" }, - { "tcp block", "blocked segments" }, - { nullptr, nullptr } -}; - -const PegInfo* Stream_GetNormPegs() -{ - return pegName; -} - -NormPegs Stream_GetNormCounts(unsigned& c) -{ - c = PC_MAX; - return normStats; -} - -static inline int SetupOK(const TcpTracker* st) -{ - return ((st->s_mgr.sub_state & SUB_SETUP_OK) == SUB_SETUP_OK); -} - -uint32_t StreamGetWindow(Flow* flow, TcpTracker* st, TcpDataBlock* tdb) -{ - int32_t window; - - if (st->l_window) - { - // don't use the window if we may have missed scaling - if (!(flow->session_state & STREAM_STATE_MIDSTREAM)) - return st->l_window; - } - // one way zero window is unitialized - // two way zero window is actually closed (regardless of scaling) - else if (flow->two_way_traffic()) - return st->l_window; - - // ensure the data is in the window - window = tdb->end_seq - st->r_win_base; - - if (window < 0) - window = 0; - - return (uint32_t) window; -} - -uint32_t StreamGetTcpTimestamp(Packet* p, uint32_t* ts, int strip) -{ - DebugMessage(DEBUG_STREAM_STATE, "Getting timestamp...\n"); - - const NormMode mode = Normalize_GetMode(NORM_TCP_OPT); - tcp::TcpOptIterator iter(p->ptrs.tcph, p); - - // using const because non-const is not supported - for (const tcp::TcpOption& opt : iter) - { - if (opt.code == tcp::TcpOptCode::TIMESTAMP) - { - if (strip) - { - NormalStripTimeStamp(p, &opt, mode); - } else if (!strip || !NormalStripTimeStamp(p, &opt, mode)) - { - *ts = EXTRACT_32BITS(opt.data); - DebugFormat(DEBUG_STREAM_STATE, "Found timestamp %lu\n", *ts); - - return TF_TSTAMP; - } - } - } - *ts = 0; - - DebugMessage(DEBUG_STREAM_STATE, "No timestamp...\n"); - - return TF_NONE; -} - -// per rfc 793 a rst is valid if the seq number is in window -// for all states but syn-sent (handled above). however, we -// validate here based on how various implementations actually -// handle a rst. -int ValidRst(Flow* flow, TcpTracker *st, TcpDataBlock *tdb) -{ - DebugFormat(DEBUG_STREAM_STATE, "Checking end_seq (%X) > r_win_base (%X) && seq (%X) < r_nxt_ack(%X)\n", - tdb->end_seq, st->r_win_base, tdb->seq, st->r_nxt_ack+StreamGetWindow(flow, st, tdb)); - - switch (st->os_policy) { - case STREAM_POLICY_HPUX11: - if (SEQ_GEQ(tdb->seq, st->r_nxt_ack)) - { - DebugMessage(DEBUG_STREAM_STATE, "rst is valid seq (>= next seq)!\n"); - return 1; - } - DebugMessage(DEBUG_STREAM_STATE, "rst is not valid seq (>= next seq)!\n"); - return 0; - break; - case STREAM_POLICY_FIRST: - case STREAM_POLICY_LAST: - case STREAM_POLICY_MACOS: - case STREAM_POLICY_WINDOWS: - case STREAM_POLICY_VISTA: - case STREAM_POLICY_WINDOWS2K3: - case STREAM_POLICY_HPUX10: - case STREAM_POLICY_IRIX: - if (SEQ_EQ(tdb->seq, st->r_nxt_ack)) - { - DebugMessage(DEBUG_STREAM_STATE, "rst is valid seq (next seq)!\n"); - return 1; - } - DebugMessage(DEBUG_STREAM_STATE, "rst is not valid seq (next seq)!\n"); - return 0; - break; - case STREAM_POLICY_BSD: - case STREAM_POLICY_LINUX: - case STREAM_POLICY_OLD_LINUX: - case STREAM_POLICY_SOLARIS: - if (SEQ_GEQ(tdb->end_seq, st->r_win_base)) - { - // reset must be admitted when window closed - if (SEQ_LEQ(tdb->seq, - st->r_win_base + StreamGetWindow(flow, st, tdb))) - { - DebugMessage(DEBUG_STREAM_STATE, "rst is valid seq (within window)!\n"); - return 1; - } - } - - DebugMessage(DEBUG_STREAM_STATE, "rst is not valid seq (within window)!\n"); - return 0; - break; - } - - DebugMessage(DEBUG_STREAM_STATE, "rst is not valid!\n"); - return 0; -} - -int ValidTimestamp(TcpTracker *talker, TcpTracker *listener, TcpDataBlock *tdb, Packet *p, - int *eventcode, int *got_ts) -{ - if ( ( p->ptrs.tcph->th_flags & TH_RST ) - or listener->config->policy == STREAM_POLICY_PROXY) - return ACTION_NOTHING; - -#if 0 - if ( p->ptrs.tcph->th_flags & TH_ACK && - Normalize_IsEnabled(NORM_TCP_OPT) ) - { - // FIXIT-L validate tsecr here (check that it was previously sent) - // checking for the most recent ts is easy enough must check if - // ts are up to date in retransmitted packets - } -#endif - /* - * check PAWS - */ - if ((talker->flags & TF_TSTAMP) && (listener->flags & TF_TSTAMP)) - { - char validate_timestamp = 1; - DebugMessage(DEBUG_STREAM_STATE, "Checking timestamps for PAWS\n"); - - *got_ts = StreamGetTcpTimestamp(p, &tdb->ts, 0); - - if (*got_ts) - { - if (listener->config->policy == STREAM_POLICY_HPUX11) - { - /* HPUX 11 ignores timestamps for out of order segments */ - if ((listener->flags & TF_MISSING_PKT) - || !SEQ_EQ(listener->r_nxt_ack, tdb->seq)) - { - validate_timestamp = 0; - } - } - - if (talker->flags & TF_TSTAMP_ZERO) - { - /* Handle the case where the 3whs used a 0 timestamp. Next packet - * from that endpoint should have a valid timestamp... */ - if ((listener->config->policy == STREAM_POLICY_LINUX) - || (listener->config->policy == STREAM_POLICY_WINDOWS2K3)) - { - /* Linux, Win2k3 et al. do not support timestamps if - * the 3whs used a 0 timestamp. */ - talker->flags &= ~TF_TSTAMP; - listener->flags &= ~TF_TSTAMP; - validate_timestamp = 0; - } else if ((listener->config->policy == STREAM_POLICY_OLD_LINUX) - || (listener->config->policy == STREAM_POLICY_WINDOWS) - || (listener->config->policy == STREAM_POLICY_VISTA)) - { - /* Older Linux (2.2 kernel & earlier), Win32 (non 2K3) - * allow the 3whs to use a 0 timestamp. */ - talker->flags &= ~TF_TSTAMP_ZERO; - if (SEQ_EQ(listener->r_nxt_ack, tdb->seq)) - { - talker->ts_last = tdb->ts; - validate_timestamp = 0; /* Ignore the timestamp for this - * first packet, next one will - * checked. */ - } - } - } - - if (validate_timestamp) - { - int result = 0; - if (listener->config->policy == STREAM_POLICY_LINUX) - { - /* Linux 2.6 accepts timestamp values that are off - * by one. */ - result = (int) ((tdb->ts - talker->ts_last) + 1); - } else - { - result = (int) (tdb->ts - talker->ts_last); - } - - if (result < 0) - { - DebugMessage(DEBUG_STREAM_STATE, "Packet outside PAWS window, dropping\n"); - /* bail, we've got a packet outside the PAWS window! */ - //inc_tcp_discards(); - *eventcode |= EVENT_BAD_TIMESTAMP; - NormalDropPacketIf(p, NORM_TCP_OPT); - return ACTION_BAD_PKT; - } else if ((talker->ts_last != 0) - && ((uint32_t) p->pkth->ts.tv_sec - > talker->ts_last_pkt + PAWS_24DAYS)) - { - /* this packet is from way too far into the future */ - DebugFormat(DEBUG_STREAM_STATE, "packet PAWS timestamp way too far ahead of last packet %d %d...\n", - p->pkth->ts.tv_sec, talker->ts_last_pkt); - //inc_tcp_discards(); - *eventcode |= EVENT_BAD_TIMESTAMP; - NormalDropPacketIf(p, NORM_TCP_OPT); - return ACTION_BAD_PKT; - } else - { - DebugMessage(DEBUG_STREAM_STATE, "packet PAWS ok...\n"); - } - } - } - else - { - /* we've got a packet with no timestamp, but 3whs indicated talker - * was doing timestamps. This breaks protocol, however, some servers - * still ack the packet with the missing timestamp. Log an alert, - * but continue to process the packet - */ - *eventcode |= EVENT_NO_TIMESTAMP; - DebugMessage(DEBUG_STREAM_STATE, "packet no timestamp, had one earlier from this side...ok for now...\n"); - - if (listener->config->policy == STREAM_POLICY_SOLARIS) - { - /* Solaris stops using timestamps if it receives a packet - * without a timestamp and there were timestamps in use. - */ - listener->flags &= ~TF_TSTAMP; - } - NormalDropPacketIf(p, NORM_TCP_OPT); - } - } - else if (p->ptrs.tcph->is_syn_only()) - { - *got_ts = StreamGetTcpTimestamp(p, &tdb->ts, 0); - if (*got_ts) - talker->flags |= TF_TSTAMP; - } - else - { - // if we are not handling timestamps, and this isn't a syn - // (only), and we have seen a valid 3way setup, then we strip - // (nop) the timestamp option. this includes the cases where - // we disable timestamp handling. - int strip = (SetupOK(talker) && SetupOK(listener)); - DebugMessage(DEBUG_STREAM_STATE, "listener not doing timestamps...\n"); - *got_ts = StreamGetTcpTimestamp(p, &tdb->ts, strip); - - if (*got_ts) - { - if (!(talker->flags & TF_TSTAMP)) - { - /* Since we skipped the SYN, may have missed the talker's - * timestamp there, so set it now. - */ - talker->flags |= TF_TSTAMP; - if (tdb->ts == 0) - { - talker->flags |= TF_TSTAMP_ZERO; - } - } - - /* Only valid to test this if listener is using timestamps. - * Otherwise, timestamp in this packet is not used, regardless - * of its value. */ - if ((tdb->ts == 0) && (listener->flags & TF_TSTAMP)) - { - switch (listener->os_policy) { - case STREAM_POLICY_WINDOWS: - case STREAM_POLICY_VISTA: - case STREAM_POLICY_WINDOWS2K3: - case STREAM_POLICY_OLD_LINUX: - case STREAM_POLICY_SOLARIS: - /* Old Linux & Windows allows a 0 timestamp value. */ - break; - default: - DebugMessage(DEBUG_STREAM_STATE, "Packet with 0 timestamp, dropping\n"); - //inc_tcp_discards(); - /* bail */ - *eventcode |= EVENT_BAD_TIMESTAMP; - return ACTION_BAD_PKT; - } - } - } - } - return ACTION_NOTHING; -} - -int RepeatedSyn(TcpTracker *listener, TcpTracker *talker, TcpDataBlock *tdb, TcpSession *tcpssn) -{ - switch (listener->os_policy) - { - case STREAM_POLICY_WINDOWS: - case STREAM_POLICY_WINDOWS2K3: - case STREAM_POLICY_VISTA: - /* Windows has some strange behaviour here. If the - * sequence of the reset is the next expected sequence, - * it Resets. Otherwise it ignores the 2nd SYN. - */ - if (SEQ_EQ(tdb->seq, listener->r_nxt_ack)) - { - DebugMessage(DEBUG_STREAM_STATE, "Got syn on established windows ssn, which causes Reset, bailing\n"); - tcpssn->flow->ssn_state.session_flags |= SSNFLAG_RESET; - talker->s_mgr.state = TCP_STATE_CLOSED; - return ACTION_RST; - } else - { - DebugMessage(DEBUG_STREAM_STATE, "Got syn on established windows ssn, not causing Reset, bailing\n"); - inc_tcp_discards(); - return ACTION_NOTHING; - } - break; - - case STREAM_POLICY_MACOS: - /* MACOS ignores a 2nd SYN, regardless of the sequence number. */ - DebugMessage(DEBUG_STREAM_STATE, "Got syn on established macos ssn, not causing Reset, bailing\n"); - inc_tcp_discards(); - return ACTION_NOTHING; - break; - - case STREAM_POLICY_FIRST: - case STREAM_POLICY_LAST: - case STREAM_POLICY_LINUX: - case STREAM_POLICY_OLD_LINUX: - case STREAM_POLICY_BSD: - case STREAM_POLICY_SOLARIS: - case STREAM_POLICY_HPUX11: - case STREAM_POLICY_HPUX10: - case STREAM_POLICY_IRIX: - /* If its not a retransmission of the actual SYN... RESET */ - if (!SEQ_EQ(tdb->seq, talker->isn)) - { - DebugMessage(DEBUG_STREAM_STATE, "Got syn on established ssn, which causes Reset, bailing\n"); - tcpssn->flow->ssn_state.session_flags |= SSNFLAG_RESET; - talker->s_mgr.state = TCP_STATE_CLOSED; - return ACTION_RST; - } else - { - DebugMessage(DEBUG_STREAM_STATE, "Got syn on established ssn, not causing Reset, bailing\n"); - inc_tcp_discards(); - return ACTION_NOTHING; - } - break; - } - return ACTION_NOTHING; -} diff --git a/src/stream/tcp/tcp_normalization.h b/src/stream/tcp/tcp_normalization.h deleted file mode 100644 index 07b408a37..000000000 --- a/src/stream/tcp/tcp_normalization.h +++ /dev/null @@ -1,192 +0,0 @@ -//-------------------------------------------------------------------------- -// Copyright (C) 2014-2015 Cisco and/or its affiliates. All rights reserved. -// -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License Version 2 as published -// by the Free Software Foundation. You may not use, modify or distribute -// this program under any other version of the GNU General Public License. -// -// 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 -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License along -// with this program; if not, write to the Free Software Foundation, Inc., -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -//-------------------------------------------------------------------------- - -// tcp_normalization.h author davis mcpherson -// Created on: Jul 31, 2015 - -#ifndef TCP_NORMALIZATION_H -#define TCP_NORMALIZATION_H - -#include "main/snort_types.h" -#include "framework/counts.h" -#include "perf_monitor/perf.h" -#include "protocols/tcp_options.h" -#include "protocols/tcp.h" -#include "normalize/normalize.h" -#include "packet_io/active.h" -#include "tcp_session.h" -#include "tcp_defs.h" - -enum PegCounts -{ - PC_TCP_TRIM_SYN, - PC_TCP_TRIM_RST, - PC_TCP_TRIM_WIN, - PC_TCP_TRIM_MSS, - PC_TCP_ECN_SSN, - PC_TCP_TS_NOP, - PC_TCP_IPS_DATA, - PC_TCP_BLOCK, - PC_MAX -}; - -extern THREAD_LOCAL PegCount normStats[PC_MAX][NORM_MODE_MAX]; - -/* target-based policy types */ -// changes to this enum require changes to stream_api.h::TCP_POLICIES -#define STREAM_POLICY_FIRST 1 -#define STREAM_POLICY_LAST 2 -#define STREAM_POLICY_LINUX 3 -#define STREAM_POLICY_OLD_LINUX 4 -#define STREAM_POLICY_BSD 5 -#define STREAM_POLICY_MACOS 6 -#define STREAM_POLICY_SOLARIS 7 -#define STREAM_POLICY_IRIX 8 -#define STREAM_POLICY_HPUX11 9 -#define STREAM_POLICY_HPUX10 10 -#define STREAM_POLICY_WINDOWS 11 -#define STREAM_POLICY_WINDOWS2K3 12 -#define STREAM_POLICY_VISTA 13 -#define STREAM_POLICY_PROXY 14 -#define STREAM_POLICY_DEFAULT STREAM_POLICY_BSD - -//----------------------------------------------------------------------- -// instead of centralizing all these normalizations so that -// Normalize_IsEnabled() is called only once, the checks and -// normalizations are localized. this should lead to many -// fewer total checks. however, it is best to minimize -// configuration checks on a per packet basis so there is -// still room for improvement. -static inline bool NormalDropPacketIf(Packet* p, NormFlags f) -{ - const NormMode mode = Normalize_GetMode(f); - - normStats[PC_TCP_BLOCK][mode]++; - sfBase.iPegs[PERF_COUNT_TCP_BLOCK][mode]++; - - if (mode == NORM_MODE_ON) - { - Active::drop_packet(p); - return true; - } - return false; -} - -static inline bool NormalStripTimeStamp(Packet* p, const tcp::TcpOption* opt, NormMode mode) -{ - normStats[PC_TCP_TS_NOP][mode]++; - sfBase.iPegs[PERF_COUNT_TCP_TS_NOP][mode]++; - - if (mode == NORM_MODE_ON) - { - // set raw option bytes to nops - memset( ( void * ) opt, ( uint32_t ) tcp::TcpOptCode::NOP, tcp::TCPOLEN_TIMESTAMP); - p->packet_flags |= PKT_MODIFIED; - return true; - } - return false; -} - -static inline void NormalTrimPayload(Packet* p, uint16_t max, TcpDataBlock* tdb) -{ - uint16_t fat = p->dsize - max; - p->dsize = max; - p->packet_flags |= (PKT_MODIFIED | PKT_RESIZED); - tdb->end_seq -= fat; -} - -static inline void NormalTrimPayloadIf(Packet* p, uint32_t max, TcpDataBlock* tdb, NormFlags flag, - PegCounts peg, PerfCounts perf) -{ - const NormMode mode = Normalize_GetMode(flag); - - if (mode == NORM_MODE_ON) - NormalTrimPayload(p, max, tdb); - - normStats[peg][mode]++; - sfBase.iPegs[perf][mode]++; -} - -static inline void NormalTrimPayloadIfSyn(Packet* p, uint32_t max, TcpDataBlock* tdb) -{ - if (p->dsize > max) - NormalTrimPayloadIf(p, max, tdb, NORM_TCP_TRIM_SYN, - PC_TCP_TRIM_SYN, PERF_COUNT_TCP_TRIM_SYN); -} - -static inline void NormalTrimPayloadIfRst(Packet* p, uint32_t max, TcpDataBlock* tdb) -{ - if (p->dsize > max) - NormalTrimPayloadIf(p, max, tdb, NORM_TCP_TRIM_RST, PC_TCP_TRIM_RST, - PERF_COUNT_TCP_TRIM_RST); -} - -static inline void NormalTrimPayloadIfWin(Packet* p, uint32_t max, TcpDataBlock* tdb) -{ - if (p->dsize > max) - NormalTrimPayloadIf(p, max, tdb, NORM_TCP_TRIM_WIN, PC_TCP_TRIM_WIN, - PERF_COUNT_TCP_TRIM_WIN); -} - -static inline void NormalTrimPayloadIfMss(Packet* p, uint32_t max, TcpDataBlock* tdb) -{ - if (p->dsize > max) - NormalTrimPayloadIf(p, max, tdb, NORM_TCP_TRIM_MSS, PC_TCP_TRIM_MSS, - PERF_COUNT_TCP_TRIM_MSS); -} - -static inline void NormalTrackECN(TcpSession* s, tcp::TCPHdr* tcph, int req3way) -{ - if (!s) - return; - - if (tcph->is_syn_ack()) - { - if (!req3way || s->ecn) - s->ecn = ((tcph->th_flags & (TH_ECE | TH_CWR)) == TH_ECE); - } else if (tcph->is_syn()) - s->ecn = tcph->are_flags_set(TH_ECE | TH_CWR); -} - -static inline void NormalCheckECN(TcpSession* s, Packet* p) -{ - if (!s->ecn && (p->ptrs.tcph->th_flags & (TH_ECE | TH_CWR))) - { - const NormMode mode = Normalize_GetMode(NORM_TCP_ECN_STR); - - if (mode == NORM_MODE_ON) - { - ((tcp::TCPHdr*) p->ptrs.tcph)->th_flags &= ~(TH_ECE | TH_CWR); - p->packet_flags |= PKT_MODIFIED; - } - normStats[PC_TCP_ECN_SSN][mode]++; - sfBase.iPegs[PERF_COUNT_TCP_ECN_SSN][mode]++; - } -} - -const PegInfo* Stream_GetNormPegs(); -NormPegs Stream_GetNormCounts(unsigned& c); - -uint32_t StreamGetWindow(Flow* flow, TcpTracker* st, TcpDataBlock* tdb); -uint32_t StreamGetTcpTimestamp(Packet* p, uint32_t* ts, int strip); -int ValidTimestamp(TcpTracker *talker, TcpTracker *listener, TcpDataBlock *tdb, Packet *p, - int *eventcode, int *got_ts); -int ValidRst(Flow* flow, TcpTracker *st, TcpDataBlock *tdb); -int RepeatedSyn(TcpTracker *listener, TcpTracker *talker, TcpDataBlock *tdb, TcpSession *tcpssn); - -#endif diff --git a/src/stream/tcp/tcp_normalizer.cc b/src/stream/tcp/tcp_normalizer.cc new file mode 100644 index 000000000..53c49f7d6 --- /dev/null +++ b/src/stream/tcp/tcp_normalizer.cc @@ -0,0 +1,407 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2015-2015 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- + +// tcp_normalization.cc author davis mcpherson +// Created on: Jul 31, 2015 + +#include "tcp_normalizer.h" +#include "tcp_events.h" + +THREAD_LOCAL PegCount normStats[PC_MAX][NORM_MODE_MAX]; + +static const PegInfo pegName[] = +{ + { "tcp trim syn", "tcp segments trimmed on SYN" }, + { "tcp trim rst", "RST packets with data trimmed" }, + { "tcp trim win", "data trimed to window" }, + { "tcp trim mss", "data trimmed to MSS" }, + { "tcp ecn session", "ECN bits cleared" }, + { "tcp ts nop", "timestamp options cleared" }, + { "tcp ips data", "normalized segments" }, + { "tcp block", "blocked segments" }, + { nullptr, nullptr } +}; + +static inline int SetupOK(const TcpTracker* st) +{ + return ((st->s_mgr.sub_state & SUB_SETUP_OK) == SUB_SETUP_OK); +} + +TcpNormalizer::TcpNormalizer( uint16_t os_policy, TcpSession* session, TcpTracker* tracker ) : + os_policy( os_policy ), session( session ), tracker( tracker ), + peer_tracker( nullptr ) +{ + trim_syn = Normalize_GetMode( NORM_TCP_TRIM_SYN ); + trim_rst = Normalize_GetMode( NORM_TCP_TRIM_RST ); + trim_win = Normalize_GetMode( NORM_TCP_TRIM_WIN ); + trim_mss = Normalize_GetMode( NORM_TCP_TRIM_MSS ); + strip_ecn = Normalize_GetMode( NORM_TCP_ECN_STR ); + tcp_block = Normalize_GetMode( NORM_TCP_BLOCK ); + opt_block = Normalize_GetMode( NORM_TCP_OPT ); + + paws_ts_fudge = 0; + paws_drop_zero_ts = true; +} + +const PegInfo* TcpNormalizer::get_normalization_pegs() +{ + return pegName; +} + +NormPegs TcpNormalizer::get_normalization_counts(unsigned& c) +{ + c = PC_MAX; + return normStats; +} + +void TcpNormalizer::trim_payload(TcpDataBlock* tdb, uint32_t max, NormMode mode, PegCounts peg, PerfCounts perf) +{ + if (mode == NORM_MODE_ON) + { + uint16_t fat = tdb->pkt->dsize - max; + tdb->pkt->dsize = max; + tdb->pkt->packet_flags |= (PKT_MODIFIED | PKT_RESIZED); + tdb->end_seq -= fat; + } + + normStats[peg][mode]++; + sfBase.iPegs[perf][mode]++; +} + +bool TcpNormalizer::strip_tcp_timestamp(TcpDataBlock* tdb, const tcp::TcpOption* opt, NormMode mode) +{ + normStats[PC_TCP_TS_NOP][mode]++; + sfBase.iPegs[PERF_COUNT_TCP_TS_NOP][mode]++; + + if (mode == NORM_MODE_ON) + { + // set raw option bytes to nops + memset((void *) opt, (uint32_t) tcp::TcpOptCode::NOP, tcp::TCPOLEN_TIMESTAMP); + tdb->pkt->packet_flags |= PKT_MODIFIED; + return true; + } + + return false; + } +bool TcpNormalizer::packet_dropper(TcpDataBlock* tdb, NormFlags f) +{ + const NormMode mode = (f == NORM_TCP_BLOCK) ? tcp_block : opt_block; + + normStats[PC_TCP_BLOCK][mode]++; + sfBase.iPegs[PERF_COUNT_TCP_BLOCK][mode]++; + + if (mode == NORM_MODE_ON) + { + Active::drop_packet(tdb->pkt); + return true; + } + + return false; +} + +void TcpNormalizer::trim_syn_payload(TcpDataBlock* tdb, uint32_t max) +{ + if (tdb->pkt->dsize > max) + trim_payload(tdb, max, trim_syn, PC_TCP_TRIM_SYN, PERF_COUNT_TCP_TRIM_SYN); +} + +void TcpNormalizer::trim_rst_payload(TcpDataBlock* tdb, uint32_t max) +{ + if (tdb->pkt->dsize > max) + trim_payload(tdb, max, trim_rst, PC_TCP_TRIM_RST, PERF_COUNT_TCP_TRIM_RST); +} + +void TcpNormalizer::trim_win_payload(TcpDataBlock* tdb, uint32_t max) +{ + if (tdb->pkt->dsize > max) + trim_payload(tdb, max, trim_win, PC_TCP_TRIM_WIN, PERF_COUNT_TCP_TRIM_WIN); +} + +void TcpNormalizer::trim_mss_payload(TcpDataBlock* tdb, uint32_t max) +{ + if (tdb->pkt->dsize > max) + trim_payload(tdb, max, trim_mss, PC_TCP_TRIM_MSS, PERF_COUNT_TCP_TRIM_MSS); +} + +void TcpNormalizer::ecn_tracker( tcp::TCPHdr* tcph, bool req3way ) +{ + if( tcph->is_syn_ack() ) + { + if( !req3way || session->ecn ) + session->ecn = ((tcph->th_flags & (TH_ECE | TH_CWR)) == TH_ECE); + } + else if( tcph->is_syn() ) + session->ecn = tcph->are_flags_set(TH_ECE | TH_CWR); +} + +void TcpNormalizer::ecn_stripper( Packet* p ) +{ + if (!session->ecn && (p->ptrs.tcph->th_flags & (TH_ECE | TH_CWR))) + { + if (strip_ecn == NORM_MODE_ON) + { + ((tcp::TCPHdr*) p->ptrs.tcph)->th_flags &= ~(TH_ECE | TH_CWR); + p->packet_flags |= PKT_MODIFIED; + } + + normStats[PC_TCP_ECN_SSN][strip_ecn]++; + sfBase.iPegs[PERF_COUNT_TCP_ECN_SSN][strip_ecn]++; + } +} + +// don't use the window if we may have missed scaling +// one way zero window is unitialized +// two way zero window is actually closed (regardless of scaling) +uint32_t TcpNormalizer::get_stream_window( TcpDataBlock* tdb ) +{ + int32_t window; + + if( tracker->l_window ) + { + if( !(session->flow->session_state & STREAM_STATE_MIDSTREAM ) ) + return tracker->l_window; + } + else if( session->flow->two_way_traffic() ) + return tracker->l_window; + + // ensure the data is in the window + window = tdb->end_seq - tracker->r_win_base; + if (window < 0) + window = 0; + + return (uint32_t) window; +} + +uint32_t TcpNormalizer::get_tcp_timestamp(TcpDataBlock* tdb, bool strip) +{ + DebugMessage(DEBUG_STREAM_STATE, "Getting timestamp...\n"); + + tcp::TcpOptIterator iter(tdb->pkt->ptrs.tcph, tdb->pkt); + + // using const because non-const is not supported + for (const tcp::TcpOption& opt : iter) + { + if (opt.code == tcp::TcpOptCode::TIMESTAMP) + { + bool stripped = false; + + if (strip) + stripped = strip_tcp_timestamp(tdb, &opt, opt_block); + + if(!stripped) + { + tdb->ts = EXTRACT_32BITS(opt.data); + DebugFormat(DEBUG_STREAM_STATE, "Found timestamp %lu\n", *ts); + + return TF_TSTAMP; + } + } + } + tdb->ts = 0; + + DebugMessage(DEBUG_STREAM_STATE, "No timestamp...\n"); + + return TF_NONE; +} + +bool TcpNormalizer::validate_rst_seq_geq( TcpDataBlock* tdb ) +{ + DebugFormat(DEBUG_STREAM_STATE, "Checking end_seq (%X) > r_win_base (%X) && seq (%X) < r_nxt_ack(%X)\n", + tdb->end_seq, tracker->r_win_base, tdb->seq, tracker->r_nxt_ack + get_stream_window( tdb )); + + if (SEQ_GEQ(tdb->seq, tracker->r_nxt_ack)) + { + DebugMessage(DEBUG_STREAM_STATE, "rst is valid seq (>= next seq)!\n"); + return true; + } + + DebugMessage(DEBUG_STREAM_STATE, "rst is not valid seq (>= next seq)!\n"); + return false; +} + +bool TcpNormalizer::validate_rst_end_seq_geq( TcpDataBlock* tdb ) +{ + DebugFormat(DEBUG_STREAM_STATE, "Checking end_seq (%X) > r_win_base (%X) && seq (%X) < r_nxt_ack(%X)\n", + tdb->end_seq, tracker->r_win_base, tdb->seq, tracker->r_nxt_ack + get_stream_window( tdb )); + + if (SEQ_GEQ(tdb->end_seq, tracker->r_win_base)) + { + // reset must be admitted when window closed + if (SEQ_LEQ(tdb->seq, tracker->r_win_base + get_stream_window( tdb ))) + { + DebugMessage(DEBUG_STREAM_STATE, "rst is valid seq (within window)!\n"); + return true; + } + } + + DebugMessage(DEBUG_STREAM_STATE, "rst is not valid seq (within window)!\n"); + return false; +} + +bool TcpNormalizer::validate_rst_seq_eq( TcpDataBlock* tdb ) +{ + DebugFormat(DEBUG_STREAM_STATE, "Checking end_seq (%X) > r_win_base (%X) && seq (%X) < r_nxt_ack(%X)\n", + tdb->end_seq, tracker->r_win_base, tdb->seq, tracker->r_nxt_ack + get_stream_window( tdb )); + + if (SEQ_EQ(tdb->seq, tracker->r_nxt_ack)) + { + DebugMessage(DEBUG_STREAM_STATE, "rst is valid seq (next seq)!\n"); + return true; + } + + DebugMessage(DEBUG_STREAM_STATE, "rst is not valid seq (next seq)!\n"); + return false; +} + +// per rfc 793 a rst is valid if the seq number is in window +// for all states but syn-sent (handled above). however, we +// validate here based on how various implementations actually +// handle a rst. +bool TcpNormalizer::validate_rst( TcpDataBlock* tdb ) +{ + return validate_rst_seq_eq( tdb ); +} + +int TcpNormalizer::validate_paws_timestamp( TcpDataBlock* tdb, int* eventcode ) +{ + if( ( (int) ( ( tdb->ts - peer_tracker->ts_last ) + paws_ts_fudge ) ) < 0 ) + { + DebugMessage(DEBUG_STREAM_STATE, "Packet outside PAWS window, dropping\n"); + /* bail, we've got a packet outside the PAWS window! */ + //inc_tcp_discards(); + *eventcode |= EVENT_BAD_TIMESTAMP; + packet_dropper(tdb, NORM_TCP_OPT); + return ACTION_BAD_PKT; + } + else if( ( peer_tracker->ts_last != 0 ) + && ( ( uint32_t ) tdb->pkt->pkth->ts.tv_sec > peer_tracker->ts_last_pkt + PAWS_24DAYS ) ) + { + /* this packet is from way too far into the future */ + DebugFormat(DEBUG_STREAM_STATE, "packet PAWS timestamp way too far ahead of last packet %d %d...\n", + p->pkth->ts.tv_sec, peer_tracker->ts_last_pkt); + //inc_tcp_discards(); + *eventcode |= EVENT_BAD_TIMESTAMP; + packet_dropper(tdb, NORM_TCP_OPT); + return ACTION_BAD_PKT; + } + else + { + DebugMessage(DEBUG_STREAM_STATE, "packet PAWS ok...\n"); + return ACTION_NOTHING; + } +} + +bool TcpNormalizer::is_paws_ts_checked_required( TcpDataBlock* ) +{ + return true; +} + +int TcpNormalizer::validate_paws( TcpDataBlock* tdb, int* eventcode, int* got_ts ) +{ + *got_ts = get_tcp_timestamp(tdb, false); + if (*got_ts) + { + bool check_ts = is_paws_ts_checked_required( tdb ); + + if (check_ts) + return validate_paws_timestamp( tdb, eventcode ); + else + return ACTION_NOTHING; + } + else + { + // we've got a packet with no timestamp, but 3whs indicated talker was doing + // timestamps. This breaks protocol, however, some servers still ack the packet + // with the missing timestamp. Log an alert, but continue to process the packet + DebugMessage(DEBUG_STREAM_STATE, "packet no timestamp, had one earlier from this side...ok for now...\n"); + *eventcode |= EVENT_NO_TIMESTAMP; + + /* Ignore the timestamp for this first packet, next one will checked. */ + if (tracker->config->policy == STREAM_POLICY_SOLARIS) + tracker->flags &= ~TF_TSTAMP; + + packet_dropper(tdb, NORM_TCP_OPT); + return ACTION_NOTHING; + } +} + +int TcpNormalizer::handle_paws_no_timestamps(TcpDataBlock* tdb, int* eventcode, int* got_ts) +{ + // if we are not handling timestamps, and this isn't a syn (only), and we have seen a + // valid 3way setup, then we strip (nop) the timestamp option. this includes the cases + // where we disable timestamp handling. + int strip = ( SetupOK( peer_tracker ) && SetupOK( tracker ) ); + DebugMessage(DEBUG_STREAM_STATE, "listener not doing timestamps...\n"); + + *got_ts = get_tcp_timestamp(tdb, strip); + if (*got_ts) + { + if (!(peer_tracker->flags & TF_TSTAMP)) + { + // SYN skipped, may have missed talker's timestamp , so set it now. + peer_tracker->flags |= TF_TSTAMP; + if (tdb->ts == 0) + peer_tracker->flags |= TF_TSTAMP_ZERO; + } + + // Only valid to test this if listener is using timestamps. Otherwise, timestamp + // in this packet is not used, regardless of its value. + if ( ( paws_drop_zero_ts && ( tdb->ts == 0 ) ) && ( tracker->flags & TF_TSTAMP ) ) + { + DebugMessage(DEBUG_STREAM_STATE, "Packet with 0 timestamp, dropping\n"); + *eventcode |= EVENT_BAD_TIMESTAMP; + return ACTION_BAD_PKT; + } + } + + return ACTION_NOTHING; +} + +int TcpNormalizer::handle_paws(TcpDataBlock* tdb, int* eventcode, int* got_ts) +{ + if ( tdb->pkt->ptrs.tcph->is_rst() ) + return ACTION_NOTHING; + +#if 0 + if ( tdb->pkt->ptrs.tcph->is_ack() && Normalize_IsEnabled(NORM_TCP_OPT) ) + { + // FIXIT-L validate tsecr here (check that it was previously sent) + // checking for the most recent ts is easy enough must check if + // ts are up to date in retransmitted packets + } +#endif + + if ((peer_tracker->flags & TF_TSTAMP) && (tracker->flags & TF_TSTAMP)) + { + DebugMessage(DEBUG_STREAM_STATE, "Checking timestamps for PAWS\n"); + return validate_paws( tdb, eventcode, got_ts ); + } + else if (tdb->pkt->ptrs.tcph->is_syn_only()) + { + *got_ts = get_tcp_timestamp(tdb, 0); + if (*got_ts) + peer_tracker->flags |= TF_TSTAMP; + + return ACTION_NOTHING; + } + else + { + return handle_paws_no_timestamps( tdb, eventcode, got_ts ); + } +} + diff --git a/src/stream/tcp/tcp_normalizer.h b/src/stream/tcp/tcp_normalizer.h new file mode 100644 index 000000000..c09a5c0c9 --- /dev/null +++ b/src/stream/tcp/tcp_normalizer.h @@ -0,0 +1,148 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2015-2015 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- + +// tcp_normalizer.h author davis mcpherson +// Created on: Jul 31, 2015 + +#ifndef TCP_NORMALIZER_H +#define TCP_NORMALIZER_H + +#include "main/snort_types.h" +#include "framework/counts.h" +#include "perf_monitor/perf.h" +#include "protocols/tcp_options.h" +#include "protocols/tcp.h" +#include "normalize/normalize.h" +#include "packet_io/active.h" +#include "tcp_session.h" +#include "tcp_defs.h" + +enum PegCounts +{ + PC_TCP_TRIM_SYN, + PC_TCP_TRIM_RST, + PC_TCP_TRIM_WIN, + PC_TCP_TRIM_MSS, + PC_TCP_ECN_SSN, + PC_TCP_TS_NOP, + PC_TCP_IPS_DATA, + PC_TCP_BLOCK, + PC_MAX +}; + +extern THREAD_LOCAL PegCount normStats[PC_MAX][NORM_MODE_MAX]; + +class TcpNormalizer +{ +public: + + virtual ~TcpNormalizer( ) { } + + virtual bool packet_dropper (TcpDataBlock*, NormFlags ); + virtual void trim_syn_payload( TcpDataBlock*, uint32_t max = 0 ); + virtual void trim_rst_payload( TcpDataBlock*, uint32_t max = 0 ); + virtual void trim_win_payload( TcpDataBlock*, uint32_t max = 0 ); + virtual void trim_mss_payload( TcpDataBlock*, uint32_t max = 0 ); + virtual void ecn_tracker( tcp::TCPHdr*, bool req3way ); + virtual void ecn_stripper( Packet* ); + + virtual uint32_t get_stream_window( TcpDataBlock* ); + virtual uint32_t get_tcp_timestamp( TcpDataBlock *, bool strip ); + virtual int handle_paws( TcpDataBlock*, int*, int* ); + virtual bool validate_rst( TcpDataBlock* ); + virtual int handle_repeated_syn( TcpDataBlock* ) = 0; + + static const PegInfo* get_normalization_pegs( void ); + static NormPegs get_normalization_counts( unsigned& ); + + void set_peer_tracker( TcpTracker* peer_tracker ) + { + this->peer_tracker = peer_tracker; + } + + uint16_t get_os_policy() const + { + return os_policy; + } + + bool is_paws_drop_zero_ts() const + { + return paws_drop_zero_ts; + } + + int32_t get_paws_ts_fudge() const + { + return paws_ts_fudge; + } + + NormMode get_opt_block() const + { + return opt_block; + } + + NormMode get_strip_ecn() const + { + return strip_ecn; + } + + NormMode get_tcp_block() const + { + return tcp_block; + } + + NormMode get_trim_mss() const + { + return trim_mss; + } + + NormMode get_trim_win() const + { + return trim_win; + } + +protected: + TcpNormalizer( uint16_t, TcpSession*, TcpTracker* ); + virtual void trim_payload( TcpDataBlock*, uint32_t, NormMode, PegCounts, PerfCounts ); + virtual bool strip_tcp_timestamp( TcpDataBlock*, const tcp::TcpOption*, NormMode ); + virtual bool validate_rst_seq_geq( TcpDataBlock* ); + virtual bool validate_rst_end_seq_geq( TcpDataBlock* ); + virtual bool validate_rst_seq_eq( TcpDataBlock* ); + + int validate_paws_timestamp( TcpDataBlock*, int* ); + virtual bool is_paws_ts_checked_required( TcpDataBlock* ); + int validate_paws( TcpDataBlock*, int*, int* ); + int handle_paws_no_timestamps( TcpDataBlock*, int* , int* ); + + uint16_t os_policy; + TcpSession* session; + TcpTracker* tracker; + TcpTracker* peer_tracker; + + NormMode trim_syn; + NormMode trim_rst; + NormMode trim_win; + NormMode trim_mss; + NormMode strip_ecn; + NormMode tcp_block; + NormMode opt_block; + + int32_t paws_ts_fudge; + bool paws_drop_zero_ts; +}; + +#endif diff --git a/src/stream/tcp/tcp_normalizers.cc b/src/stream/tcp/tcp_normalizers.cc new file mode 100644 index 000000000..21b1babc9 --- /dev/null +++ b/src/stream/tcp/tcp_normalizers.cc @@ -0,0 +1,321 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2015-2015 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- + +// tcp_normalizers.cc author davis mcpherson +// Created on: Sep 22, 2015 + +#include "tcp_module.h" +#include "tcp_normalizers.h" + +TcpNormalizer* TcpNormalizerFactory::allocate_normalizer( uint16_t os_policy, + TcpSession* session, TcpTracker* tracker, TcpTracker* peer ) +{ + TcpNormalizer* normalizer; + + switch (os_policy) + { + case STREAM_POLICY_FIRST: + normalizer = new TcpNormalizerFirst( session, tracker ); + break; + + case STREAM_POLICY_LAST: + normalizer = new TcpNormalizerLast( session, tracker ); + break; + + case STREAM_POLICY_LINUX: + normalizer = new TcpNormalizerLinux( session, tracker ); + break; + + case STREAM_POLICY_OLD_LINUX: + normalizer = new TcpNormalizerOldLinux( session, tracker ); + break; + + case STREAM_POLICY_BSD: + normalizer = new TcpNormalizerBSD( session, tracker ); + break; + + case STREAM_POLICY_MACOS: + normalizer = new TcpNormalizerMacOS( session, tracker ); + break; + + case STREAM_POLICY_SOLARIS: + normalizer = new TcpNormalizerSolaris( session, tracker ); + break; + + case STREAM_POLICY_IRIX: + normalizer = new TcpNormalizerIrix( session, tracker ); + break; + + case STREAM_POLICY_HPUX11: + normalizer = new TcpNormalizerHpux11( session, tracker ); + break; + + case STREAM_POLICY_HPUX10: + normalizer = new TcpNormalizerHpux10( session, tracker ); + break; + + case STREAM_POLICY_WINDOWS: + normalizer = new TcpNormalizerWindows( session, tracker ); + break; + + case STREAM_POLICY_WINDOWS2K3: + normalizer = new TcpNormalizerWindows2K3( session, tracker ); + break; + + case STREAM_POLICY_VISTA: + normalizer = new TcpNormalizerVista( session, tracker ); + break; + + case STREAM_POLICY_PROXY: + normalizer = new TcpNormalizerProxy( session, tracker ); + break; + + default: + normalizer = new TcpNormalizerBSD( session, tracker ); + break; + } + + normalizer->set_peer_tracker( peer ); + return normalizer; +} + +static inline int handle_repeated_syn_mswin( TcpTracker* talker, TcpTracker* listener, + TcpDataBlock* tdb, TcpSession* session ) +{ + /* Windows has some strange behaviour here. If the sequence of the reset is the + * next expected sequence, it Resets. Otherwise it ignores the 2nd SYN. + */ + if (SEQ_EQ(tdb->seq, listener->r_nxt_ack)) + { + DebugMessage(DEBUG_STREAM_STATE, "Got syn on established windows ssn, which causes Reset, bailing\n"); + session->flow->set_session_flags( SSNFLAG_RESET ); + talker->s_mgr.state = TCP_STATE_CLOSED; + return ACTION_RST; + } + else + { + DebugMessage(DEBUG_STREAM_STATE, "Got syn on established windows ssn, not causing Reset, bailing\n"); + inc_tcp_discards(); + return ACTION_NOTHING; + } +} + +static inline int handle_repeated_syn_bsd( TcpTracker* talker, TcpDataBlock* tdb, TcpSession* session ) +{ + /* If its not a retransmission of the actual SYN... RESET */ + if (!SEQ_EQ(tdb->seq, talker->isn)) + { + DebugMessage(DEBUG_STREAM_STATE, "Got syn on established ssn, which causes Reset, bailing\n"); + session->flow->set_session_flags( SSNFLAG_RESET ); + talker->s_mgr.state = TCP_STATE_CLOSED; + return ACTION_RST; + } + else + { + DebugMessage(DEBUG_STREAM_STATE, "Got syn on established ssn, not causing Reset, bailing\n"); + inc_tcp_discards(); + return ACTION_NOTHING; + } +} + +// Linux, Win2k3 et al. do not support timestamps if the 3whs used a 0 timestamp. +static inline bool paws_3whs_zero_ts_not_supported(TcpTracker* talker, TcpTracker* listener ) +{ + bool check_ts = true; + + if (talker->flags & TF_TSTAMP_ZERO) + { + talker->flags &= ~TF_TSTAMP; + listener->flags &= ~TF_TSTAMP; + check_ts = false; + } + + return check_ts; +} + +// Older Linux ( <= 2.2 kernel ), Win32 (non 2K3) allow the 3whs to use a 0 timestamp. +static inline bool paws_3whs_zero_ts_supported(TcpTracker* talker, TcpTracker* listener, TcpDataBlock* tdb) +{ + bool check_ts = true; + + if( talker->flags & TF_TSTAMP_ZERO ) + { + talker->flags &= ~TF_TSTAMP_ZERO; + if( SEQ_EQ( listener->r_nxt_ack, tdb->seq ) ) + { + // Ignore timestamp for this first packet, save to check on next + talker->ts_last = tdb->ts; + check_ts = false; + } + } + + return check_ts; +} + +int TcpNormalizerFirst::handle_repeated_syn( TcpDataBlock* tdb ) +{ + return handle_repeated_syn_bsd( peer_tracker, tdb, session ); +} + +int TcpNormalizerLast::handle_repeated_syn( TcpDataBlock* tdb ) +{ + return handle_repeated_syn_bsd( peer_tracker, tdb, session ); +} + +bool TcpNormalizerLinux::validate_rst( TcpDataBlock *tdb ) +{ + return validate_rst_end_seq_geq( tdb ); +} + +bool TcpNormalizerLinux::is_paws_ts_checked_required( TcpDataBlock* ) +{ + return paws_3whs_zero_ts_not_supported( peer_tracker, tracker ); +} + +int TcpNormalizerLinux::handle_repeated_syn( TcpDataBlock* tdb ) +{ + return handle_repeated_syn_bsd( peer_tracker, tdb, session ); +} + +bool TcpNormalizerOldLinux::validate_rst( TcpDataBlock *tdb ) +{ + return validate_rst_end_seq_geq( tdb ); +} + +bool TcpNormalizerOldLinux::is_paws_ts_checked_required( TcpDataBlock* tdb) +{ + return paws_3whs_zero_ts_supported( peer_tracker, tracker, tdb ); +} + +int TcpNormalizerOldLinux::handle_repeated_syn( TcpDataBlock* tdb ) +{ + return handle_repeated_syn_bsd( peer_tracker, tdb, session ); +} + +bool TcpNormalizerBSD::validate_rst( TcpDataBlock *tdb ) +{ + return validate_rst_end_seq_geq( tdb ); +} + +int TcpNormalizerBSD::handle_repeated_syn( TcpDataBlock* tdb ) +{ + return handle_repeated_syn_bsd( peer_tracker, tdb, session ); +} + +int TcpNormalizerMacOS::handle_repeated_syn( TcpDataBlock* ) +{ + /* MACOS ignores a 2nd SYN, regardless of the sequence number. */ + DebugMessage(DEBUG_STREAM_STATE, "Got syn on established macos ssn, not causing Reset, bailing\n"); + inc_tcp_discards(); + return ACTION_NOTHING; +} + +bool TcpNormalizerSolaris::validate_rst( TcpDataBlock* tdb ) +{ + return validate_rst_end_seq_geq( tdb ); +} + +int TcpNormalizerSolaris::handle_repeated_syn( TcpDataBlock* tdb ) +{ + return handle_repeated_syn_bsd( peer_tracker, tdb, session ); +} + +int TcpNormalizerIrix::handle_repeated_syn( TcpDataBlock* tdb ) +{ + return handle_repeated_syn_bsd( peer_tracker, tdb, session ); +} + +bool TcpNormalizerHpux11::validate_rst( TcpDataBlock *tdb ) +{ + return validate_rst_seq_geq( tdb ); +} + +bool TcpNormalizerHpux11::is_paws_ts_checked_required( TcpDataBlock* tdb ) +{ + /* HPUX 11 ignores timestamps for out of order segments */ + if ((tracker->flags & TF_MISSING_PKT) || !SEQ_EQ(tracker->r_nxt_ack, tdb->seq)) + return false; + else + return true; +} + +int TcpNormalizerHpux11::handle_repeated_syn( TcpDataBlock* tdb ) +{ + return handle_repeated_syn_bsd( peer_tracker, tdb, session ); +} + +int TcpNormalizerHpux10::handle_repeated_syn( TcpDataBlock* tdb ) +{ + return handle_repeated_syn_bsd( peer_tracker, tdb, session ); +} + +bool TcpNormalizerWindows::is_paws_ts_checked_required( TcpDataBlock* tdb) +{ + return paws_3whs_zero_ts_supported( peer_tracker, tracker, tdb ); +} + +int TcpNormalizerWindows::handle_repeated_syn( TcpDataBlock *tdb ) +{ + return handle_repeated_syn_mswin( peer_tracker, tracker, tdb, session ); +} + + +int TcpNormalizerWindows2K3::handle_repeated_syn( TcpDataBlock *tdb ) +{ + return handle_repeated_syn_mswin( peer_tracker, tracker, tdb, session ); +} + +bool TcpNormalizerWindows2K3::is_paws_ts_checked_required( TcpDataBlock* ) +{ + return paws_3whs_zero_ts_not_supported( peer_tracker, tracker ); +} + +bool TcpNormalizerVista::is_paws_ts_checked_required( TcpDataBlock* tdb) +{ + return paws_3whs_zero_ts_supported( peer_tracker, tracker, tdb ); +} + +int TcpNormalizerVista::handle_repeated_syn( TcpDataBlock *tdb ) +{ + return handle_repeated_syn_mswin( peer_tracker, tracker, tdb, session ); +} + +bool TcpNormalizerProxy::validate_rst( TcpDataBlock *tdb ) +{ + // FIXIT - will session->flow ever be null? i would think not, remove this check if possible + if( session->flow ) + { + DebugFormat(DEBUG_STREAM_STATE, "Proxy Normalizer - Not Valid\n end_seq (%X) > r_win_base (%X) && seq (%X) < r_nxt_ack(%X)\n", + tdb->end_seq, tracker->r_win_base, tdb->seq, tracker->r_nxt_ack + get_stream_window( tdb )); + } + + return false; +} + +int TcpNormalizerProxy::handle_paws(TcpDataBlock*, Packet*, int*, int*) +{ + return ACTION_NOTHING; +} + +int TcpNormalizerProxy::handle_repeated_syn( TcpDataBlock* ) +{ + return ACTION_NOTHING; +} + + + diff --git a/src/stream/tcp/tcp_normalizers.h b/src/stream/tcp/tcp_normalizers.h new file mode 100644 index 000000000..577e64197 --- /dev/null +++ b/src/stream/tcp/tcp_normalizers.h @@ -0,0 +1,204 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2015-2015 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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 +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- + +// tcp_normalizers.h author davis mcpherson +// Created on: Sep 22, 2015 + +#ifndef TCP_NORMALIZERS_H +#define TCP_NORMALIZERS_H + +#include "tcp_defs.h" +#include "tcp_normalizer.h" + + +class TcpNormalizerFirst : public TcpNormalizer +{ +public: + TcpNormalizerFirst( TcpSession* session, TcpTracker* tracker ) : + TcpNormalizer( STREAM_POLICY_FIRST, session, tracker ) + { } + + int handle_repeated_syn( TcpDataBlock* ); +}; + +class TcpNormalizerLast : public TcpNormalizer +{ +public: + TcpNormalizerLast( TcpSession* session, TcpTracker* tracker ) : + TcpNormalizer( STREAM_POLICY_LAST, session, tracker ) + { } + + int handle_repeated_syn( TcpDataBlock* ); +}; + +class TcpNormalizerLinux : public TcpNormalizer +{ +public: + TcpNormalizerLinux( TcpSession* session, TcpTracker* tracker ) : + TcpNormalizer( STREAM_POLICY_LINUX, session, tracker ) + { + // Linux 2.6 accepts timestamp values that are off by one. so set fudge factor */ + paws_ts_fudge = 1; + } + + bool validate_rst( TcpDataBlock* ); + bool is_paws_ts_checked_required( TcpDataBlock* ); + int handle_repeated_syn( TcpDataBlock* ); +}; + +class TcpNormalizerOldLinux : public TcpNormalizer +{ +public: + TcpNormalizerOldLinux( TcpSession* session, TcpTracker* tracker ) : + TcpNormalizer( STREAM_POLICY_OLD_LINUX, session, tracker ) + { + paws_drop_zero_ts = false; + } + + bool validate_rst( TcpDataBlock* ); + bool is_paws_ts_checked_required( TcpDataBlock* ); + int handle_repeated_syn( TcpDataBlock* ); +}; + +class TcpNormalizerBSD : public TcpNormalizer +{ +public: + TcpNormalizerBSD( TcpSession* session, TcpTracker* tracker ) : + TcpNormalizer( STREAM_POLICY_BSD, session, tracker ) + { } + + bool validate_rst( TcpDataBlock* ); + int handle_repeated_syn( TcpDataBlock* ); +}; + +class TcpNormalizerMacOS : public TcpNormalizer +{ +public: + TcpNormalizerMacOS( TcpSession* session, TcpTracker* tracker ) : + TcpNormalizer( STREAM_POLICY_MACOS, session, tracker ) + { } + + int handle_repeated_syn( TcpDataBlock* ); +}; + +class TcpNormalizerSolaris : public TcpNormalizer +{ +public: + TcpNormalizerSolaris( TcpSession* session, TcpTracker* tracker ) : + TcpNormalizer( STREAM_POLICY_SOLARIS, session, tracker ) + { + paws_drop_zero_ts = false; + } + + bool validate_rst( TcpDataBlock* ); + int handle_repeated_syn( TcpDataBlock* ); +}; + +class TcpNormalizerIrix : public TcpNormalizer +{ +public: + TcpNormalizerIrix( TcpSession* session, TcpTracker* tracker ) : + TcpNormalizer( STREAM_POLICY_IRIX, session, tracker ) + { } + + int handle_repeated_syn( TcpDataBlock* ); +}; + +class TcpNormalizerHpux11 : public TcpNormalizer +{ +public: + TcpNormalizerHpux11( TcpSession* session, TcpTracker* tracker ) : + TcpNormalizer( STREAM_POLICY_HPUX11, session, tracker ) + { } + + bool validate_rst( TcpDataBlock* ); + bool is_paws_ts_checked_required( TcpDataBlock* ); + int handle_repeated_syn( TcpDataBlock* ); +}; + +class TcpNormalizerHpux10 : public TcpNormalizer +{ +public: + TcpNormalizerHpux10( TcpSession* session, TcpTracker* tracker ) : + TcpNormalizer( STREAM_POLICY_HPUX10, session, tracker ) + { } + + int handle_repeated_syn( TcpDataBlock* ); +}; + +class TcpNormalizerWindows : public TcpNormalizer +{ +public: + TcpNormalizerWindows( TcpSession* session, TcpTracker* tracker ) : + TcpNormalizer( STREAM_POLICY_WINDOWS, session, tracker ) + { + paws_drop_zero_ts = false; + } + + bool is_paws_ts_checked_required( TcpDataBlock* ); + int handle_repeated_syn( TcpDataBlock* ); +}; + +class TcpNormalizerWindows2K3 : public TcpNormalizer +{ +public: + TcpNormalizerWindows2K3( TcpSession* session, TcpTracker* tracker ) : + TcpNormalizer( STREAM_POLICY_WINDOWS2K3, session, tracker ) + { + paws_drop_zero_ts = false; + } + + bool is_paws_ts_checked_required( TcpDataBlock* ); + int handle_repeated_syn( TcpDataBlock* ); +}; + +class TcpNormalizerVista : public TcpNormalizer +{ +public: + TcpNormalizerVista( TcpSession* session, TcpTracker* tracker ) : + TcpNormalizer( STREAM_POLICY_VISTA, session, tracker ) + { + paws_drop_zero_ts = false; + } + + bool is_paws_ts_checked_required( TcpDataBlock*); + int handle_repeated_syn( TcpDataBlock* ); +}; + +class TcpNormalizerProxy : public TcpNormalizer +{ +public: + TcpNormalizerProxy( TcpSession* session, TcpTracker* tracker ) : + TcpNormalizer( STREAM_POLICY_PROXY, session, tracker ) + { } + + bool validate_rst(TcpDataBlock* ); + int handle_paws( TcpDataBlock*, Packet*, int*, int* ); + int handle_repeated_syn( TcpDataBlock* ); +}; + +class TcpNormalizerFactory +{ +public: + static TcpNormalizer* allocate_normalizer( uint16_t, TcpSession*, TcpTracker*, TcpTracker* ); +}; + + + + +#endif /* TCP_NORMALIZERS_H_ */ diff --git a/src/stream/tcp/tcp_reassembly.cc b/src/stream/tcp/tcp_reassembly.cc index 7b2a1cb7e..7640b72f6 100644 --- a/src/stream/tcp/tcp_reassembly.cc +++ b/src/stream/tcp/tcp_reassembly.cc @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------- -// Copyright (C) 2014-2015 Cisco and/or its affiliates. All rights reserved. +// Copyright (C) 2015-2015 Cisco and/or its affiliates. All rights reserved. // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License Version 2 as published @@ -31,7 +31,7 @@ #include "tcp_module.h" #include "tcp_session.h" #include "tcp_events.h" -#include "tcp_normalization.h" +#include "tcp_normalizer.h" #include "tcp_reassembly.h" #include "tcp_defs.h" @@ -365,7 +365,7 @@ static inline int SegmentFastTrack(TcpSegment *tail, TcpDataBlock *tdb) return 0; } -int AddStreamNode(TcpTracker *st, Packet *p, TcpDataBlock* tdb, int16_t len, uint32_t slide, +int AddStreamNode(TcpTracker *st, TcpDataBlock* tdb, int16_t len, uint32_t slide, uint32_t trunc, uint32_t seq, TcpSegment *left) { TcpSegment *ss = NULL; @@ -379,7 +379,7 @@ int AddStreamNode(TcpTracker *st, Packet *p, TcpDataBlock* tdb, int16_t len, uin */ DebugFormat(DEBUG_STREAM_STATE, "zero size TCP data after left & right trimming " "(len: %d slide: %d trunc: %d)\n", len, slide, trunc); inc_tcp_discards(); - NormalTrimPayloadIfWin(p, 0, tdb); + st->normalizer->trim_win_payload( tdb ); #ifdef DEBUG_STREAM_EX { @@ -403,7 +403,7 @@ int AddStreamNode(TcpTracker *st, Packet *p, TcpDataBlock* tdb, int16_t len, uin } // FIXIT-L don't allocate overlapped part - ss = TcpSegment::init(p, p->pkth->ts, p->data, p->dsize); + ss = TcpSegment::init(tdb->pkt, tdb->pkt->pkth->ts, tdb->pkt->data, tdb->pkt->dsize); if (!ss) return STREAM_INSERT_FAILED; @@ -414,9 +414,9 @@ int AddStreamNode(TcpTracker *st, Packet *p, TcpDataBlock* tdb, int16_t len, uin ss->ts = tdb->ts; /* handle the urg ptr */ - if (p->ptrs.tcph->th_flags & TH_URG) + if (tdb->pkt->ptrs.tcph->th_flags & TH_URG) { - if (p->ptrs.tcph->urp() < p->dsize) + if (tdb->pkt->ptrs.tcph->urp() < tdb->pkt->dsize) { switch (st->os_policy) { @@ -424,7 +424,7 @@ int AddStreamNode(TcpTracker *st, Packet *p, TcpDataBlock* tdb, int16_t len, uin case STREAM_POLICY_OLD_LINUX: /* Linux, Old linux discard data from urgent pointer If urg pointer is 0, it's treated as a 1 */ - ss->urg_offset = p->ptrs.tcph->urp(); + ss->urg_offset = tdb->pkt->ptrs.tcph->urp(); if (ss->urg_offset == 0) { ss->urg_offset = 1; @@ -444,8 +444,8 @@ int AddStreamNode(TcpTracker *st, Packet *p, TcpDataBlock* tdb, int16_t len, uin case STREAM_POLICY_IRIX: /* Others discard data from urgent pointer If urg pointer is beyond this packet, it's treated as a 0 */ - ss->urg_offset = p->ptrs.tcph->urp(); - if (ss->urg_offset > p->dsize) + ss->urg_offset = tdb->pkt->ptrs.tcph->urp(); + if (ss->urg_offset > tdb->pkt->dsize) { ss->urg_offset = 0; } @@ -458,7 +458,7 @@ int AddStreamNode(TcpTracker *st, Packet *p, TcpDataBlock* tdb, int16_t len, uin st->seg_bytes_logical += ss->size; st->total_bytes_queued += ss->size; - p->packet_flags |= PKT_STREAM_INSERT; + tdb->pkt->packet_flags |= PKT_STREAM_INSERT; DebugFormat(DEBUG_STREAM_STATE, "added %d bytes on segment list @ seq: 0x%X, total %lu, %d segments queued\n", ss->size, ss->seq, st->seg_bytes_logical, SegsToFlush(st, 0)); @@ -1460,14 +1460,14 @@ void purge_all(TcpTracker *st) st->seg_bytes_total = st->seg_bytes_logical = 0; } -int StreamQueue(TcpTracker *st, Packet *p, TcpDataBlock *tdb, TcpSession *tcpssn) +int StreamQueue(TcpTracker *st, TcpDataBlock *tdb, TcpSession *tcpssn) { TcpSegment *left = NULL; TcpSegment *right = NULL; TcpSegment *dump_me = NULL; uint32_t seq = tdb->seq; uint32_t seq_end = tdb->end_seq; - uint16_t len = p->dsize; + uint16_t len = tdb->pkt->dsize; int trunc = 0; int overlap = 0; int slide = 0; @@ -1478,8 +1478,8 @@ int StreamQueue(TcpTracker *st, Packet *p, TcpDataBlock *tdb, TcpSession *tcpssn int32_t dist_tail; uint16_t reassembly_policy; // To check for retransmitted data - const uint8_t* rdata = p->data; - uint16_t rsize = p->dsize; + const uint8_t* rdata = tdb->pkt->data; + uint16_t rsize = tdb->pkt->dsize; uint32_t rseq = tdb->seq; PROFILE_VARS; DEBUG_WRAP( @@ -1516,8 +1516,7 @@ int StreamQueue(TcpTracker *st, Packet *p, TcpDataBlock *tdb, TcpSession *tcpssn st->seglist_tail->seq, st->seglist_tail->size); // BLOCK add to existing block and/or allocate new block - ret = AddStreamNode(st, p, tdb, len, slide /* 0 */, trunc /* 0 */, seq, - left /* tail */); + ret = AddStreamNode(st, tdb, len, slide /* 0 */, trunc /* 0 */, seq, left /* tail */); MODULE_PROFILE_END(s5TcpInsertPerfStats); return ret; @@ -1641,13 +1640,13 @@ int StreamQueue(TcpTracker *st, Packet *p, TcpDataBlock *tdb, TcpSession *tcpssn case REASSEMBLY_POLICY_OLD_LINUX: case REASSEMBLY_POLICY_MACOS: DebugMessage(DEBUG_STREAM_STATE, "left overlap, honoring old data\n"); - if (SEQ_LT(left->seq, tdb->seq) && SEQ_GT(left->seq + left->size, tdb->seq + p->dsize)) + if (SEQ_LT(left->seq, tdb->seq) && SEQ_GT(left->seq + left->size, tdb->seq + tdb->pkt->dsize)) { if (ips_data == NORM_MODE_ON) { unsigned offset = tdb->seq - left->seq; - memcpy((uint8_t*) p->data, left->payload + offset, p->dsize); - p->packet_flags |= PKT_MODIFIED; + memcpy((uint8_t*) tdb->pkt->data, left->payload + offset, tdb->pkt->dsize); + tdb->pkt->packet_flags |= PKT_MODIFIED; } normStats[PC_TCP_IPS_DATA][ips_data]++; sfBase.iPegs[PERF_COUNT_TCP_IPS_DATA][ips_data]++; @@ -1658,8 +1657,8 @@ int StreamQueue(TcpTracker *st, Packet *p, TcpDataBlock *tdb, TcpSession *tcpssn { unsigned offset = tdb->seq - left->seq; unsigned length = left->seq + left->size - tdb->seq; - memcpy((uint8_t*) p->data, left->payload + offset, length); - p->packet_flags |= PKT_MODIFIED; + memcpy((uint8_t*) tdb->pkt->data, left->payload + offset, length); + tdb->pkt->packet_flags |= PKT_MODIFIED; } normStats[PC_TCP_IPS_DATA][ips_data]++; sfBase.iPegs[PERF_COUNT_TCP_IPS_DATA][ips_data]++; @@ -1722,7 +1721,7 @@ int StreamQueue(TcpTracker *st, Packet *p, TcpDataBlock *tdb, TcpSession *tcpssn * seq by + (seq + len) and * size by - (seq + len - left->seq). */ - ret = DupStreamNode(p, st, left, &right); + ret = DupStreamNode(tdb->pkt, st, left, &right); if (ret != STREAM_INSERT_OK) { /* No warning, its done in StreamSeglistAddNode */ @@ -1781,14 +1780,14 @@ int StreamQueue(TcpTracker *st, Packet *p, TcpDataBlock *tdb, TcpSession *tcpssn /* Treat sequence number overlap as a retransmission * Only check right side since left side happens rarely */ - RetransmitHandle(p, tcpssn); + RetransmitHandle(tdb->pkt, tcpssn); if (overlap < right->size) { if (right->is_retransmit(rdata, rsize, rseq)) { // All data was retransmitted - RetransmitProcess(p, tcpssn); + RetransmitProcess(tdb->pkt, tcpssn); addthis = 0; break; } @@ -1836,9 +1835,9 @@ int StreamQueue(TcpTracker *st, Packet *p, TcpDataBlock *tdb, TcpSession *tcpssn if (ips_data == NORM_MODE_ON) { unsigned offset = right->seq - tdb->seq; - unsigned length = tdb->seq + p->dsize - right->seq; - memcpy((uint8_t*) p->data + offset, right->payload, length); - p->packet_flags |= PKT_MODIFIED; + unsigned length = tdb->seq + tdb->pkt->dsize - right->seq; + memcpy((uint8_t*) tdb->pkt->data + offset, right->payload, length); + tdb->pkt->packet_flags |= PKT_MODIFIED; } normStats[PC_TCP_IPS_DATA][ips_data]++; sfBase.iPegs[PERF_COUNT_TCP_IPS_DATA][ips_data]++; @@ -1867,7 +1866,7 @@ int StreamQueue(TcpTracker *st, Packet *p, TcpDataBlock *tdb, TcpSession *tcpssn if (rsize == 0) { // All data was retransmitted - RetransmitProcess(p, tcpssn); + RetransmitProcess(tdb->pkt, tcpssn); addthis = 0; } continue; @@ -1927,8 +1926,8 @@ int StreamQueue(TcpTracker *st, Packet *p, TcpDataBlock *tdb, TcpSession *tcpssn if (ips_data == NORM_MODE_ON) { unsigned offset = right->seq - tdb->seq; - memcpy((uint8_t*) p->data + offset, right->payload, right->size); - p->packet_flags |= PKT_MODIFIED; + memcpy((uint8_t*) tdb->pkt->data + offset, right->payload, right->size); + tdb->pkt->packet_flags |= PKT_MODIFIED; } normStats[PC_TCP_IPS_DATA][ips_data]++; sfBase.iPegs[PERF_COUNT_TCP_IPS_DATA][ips_data]++; @@ -1966,7 +1965,7 @@ int StreamQueue(TcpTracker *st, Packet *p, TcpDataBlock *tdb, TcpSession *tcpssn /* insert this one, and see if we need to chunk it up Adjust slide so that is correct relative to orig seq */ slide = seq - tdb->seq; - ret = AddStreamNode(st, p, tdb, len, slide, trunc, seq, left); + ret = AddStreamNode(st, tdb, len, slide, trunc, seq, left); if (ret != STREAM_INSERT_OK) { /* no warning, already done above */ @@ -2025,7 +2024,7 @@ right_overlap_last: { /* Adjust slide so that is correct relative to orig seq */ slide = seq - tdb->seq; - ret = AddStreamNode(st, p, tdb, len, slide, trunc, seq, left); + ret = AddStreamNode(st, tdb, len, slide, trunc, seq, left); } else { diff --git a/src/stream/tcp/tcp_reassembly.h b/src/stream/tcp/tcp_reassembly.h index 464f53141..06974e800 100644 --- a/src/stream/tcp/tcp_reassembly.h +++ b/src/stream/tcp/tcp_reassembly.h @@ -1,5 +1,5 @@ //-------------------------------------------------------------------------- -// Copyright (C) 2014-2015 Cisco and/or its affiliates. All rights reserved. +// Copyright (C) 2015-2015 Cisco and/or its affiliates. All rights reserved. // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License Version 2 as published @@ -37,9 +37,9 @@ struct TcpTracker; struct TcpSegment { - static TcpSegment* init(struct Packet*, const struct timeval&, const uint8_t*, unsigned); - static void term(TcpSegment*); - bool is_retransmit(const uint8_t*, uint16_t size, uint32_t); + static TcpSegment* init( struct Packet*, const struct timeval&, const uint8_t*, unsigned ); + static void term( TcpSegment* ); + bool is_retransmit( const uint8_t*, uint16_t size, uint32_t ); uint8_t* payload; @@ -77,8 +77,8 @@ void purge_all(TcpTracker *st); int flush_stream(TcpSession *tcpssn, TcpTracker *st, Packet *p, uint32_t dir); int purge_flushed_ackd(TcpSession *tcpssn, TcpTracker *st); void FlushQueuedSegs(Flow* flow, TcpSession* tcpssn, bool clear, Packet* p = nullptr); -int StreamQueue(TcpTracker *st, Packet *p, TcpDataBlock *tdb, TcpSession *tcpssn); -int AddStreamNode(TcpTracker *st, Packet *p, TcpDataBlock* tdb, int16_t len, uint32_t slide, +int StreamQueue(TcpTracker *st, TcpDataBlock *tdb, TcpSession *tcpssn); +int AddStreamNode(TcpTracker *st, TcpDataBlock* tdb, int16_t len, uint32_t slide, uint32_t trunc, uint32_t seq, TcpSegment *left); uint32_t SegsToFlush(const TcpTracker* st, unsigned max); int CheckFlushPolicyOnData(TcpSession *, TcpTracker *, TcpTracker *, Packet *); diff --git a/src/stream/tcp/tcp_session.cc b/src/stream/tcp/tcp_session.cc index eaf22cd1d..341092888 100644 --- a/src/stream/tcp/tcp_session.cc +++ b/src/stream/tcp/tcp_session.cc @@ -58,7 +58,6 @@ // in specific files ... these functional groups will be further refactored as the stream tcp // rewrite continues... #include "tcp_events.h" -#include "tcp_normalization.h" #include "tcp_reassembly.h" #include "tcp_debug_trace.h" @@ -67,6 +66,7 @@ #include "tcp_listen_state.h" #include "tcp_syn_sent_state.h" #include "tcp_syn_recv_state.h" +#include "tcp_normalizers.h" // TBD-EDM #include "main/snort_types.h" @@ -114,7 +114,7 @@ using namespace tcp; /* P R O T O T Y P E S ********************************************/ -static int ProcessTcp(Flow*, Packet*, TcpDataBlock*, StreamTcpConfig*); +static int ProcessTcp(Flow*, TcpDataBlock*, StreamTcpConfig*); /* G L O B A L S **************************************************/ @@ -166,28 +166,29 @@ void StreamUpdatePerfBaseState(SFBASE *sf_base, Flow *flow, char newState) if (!flow) return; + uint32_t session_flags = flow->get_session_flags(); switch (newState) { case TCP_STATE_SYN_SENT: - if (!(flow->ssn_state.session_flags & SSNFLAG_COUNTED_INITIALIZE)) + if (!(session_flags & SSNFLAG_COUNTED_INITIALIZE)) { sf_base->iSessionsInitializing++; - flow->ssn_state.session_flags |= SSNFLAG_COUNTED_INITIALIZE; + session_flags |= SSNFLAG_COUNTED_INITIALIZE; } break; case TCP_STATE_ESTABLISHED: - if (!(flow->ssn_state.session_flags & SSNFLAG_COUNTED_ESTABLISH)) + if (!(session_flags & SSNFLAG_COUNTED_ESTABLISH)) { sf_base->iSessionsEstablished++; if (perfmon_config && (perfmon_config->perf_flags & SFPERF_FLOWIP)) UpdateFlowIPState(&sfFlow, &flow->client_ip, &flow->server_ip, SFS_STATE_TCP_ESTABLISHED); - flow->ssn_state.session_flags |= SSNFLAG_COUNTED_ESTABLISH; + session_flags |= SSNFLAG_COUNTED_ESTABLISH; - if ((flow->ssn_state.session_flags & SSNFLAG_COUNTED_INITIALIZE) - && !(flow->ssn_state.session_flags & SSNFLAG_COUNTED_CLOSING)) + if ((session_flags & SSNFLAG_COUNTED_INITIALIZE) + && !(session_flags & SSNFLAG_COUNTED_CLOSING)) { assert(sf_base->iSessionsInitializing); sf_base->iSessionsInitializing--; @@ -196,12 +197,12 @@ void StreamUpdatePerfBaseState(SFBASE *sf_base, Flow *flow, char newState) break; case TCP_STATE_CLOSING: - if (!(flow->ssn_state.session_flags & SSNFLAG_COUNTED_CLOSING)) + if (!(session_flags & SSNFLAG_COUNTED_CLOSING)) { sf_base->iSessionsClosing++; - flow->ssn_state.session_flags |= SSNFLAG_COUNTED_CLOSING; + session_flags |= SSNFLAG_COUNTED_CLOSING; - if (flow->ssn_state.session_flags & SSNFLAG_COUNTED_ESTABLISH) + if (session_flags & SSNFLAG_COUNTED_ESTABLISH) { assert(sf_base->iSessionsEstablished); sf_base->iSessionsEstablished--; @@ -209,7 +210,7 @@ void StreamUpdatePerfBaseState(SFBASE *sf_base, Flow *flow, char newState) if (perfmon_config && (perfmon_config->perf_flags & SFPERF_FLOWIP)) UpdateFlowIPState(&sfFlow, &flow->client_ip, &flow->server_ip, SFS_STATE_TCP_CLOSED); } - else if (flow->ssn_state.session_flags & SSNFLAG_COUNTED_INITIALIZE) + else if (session_flags & SSNFLAG_COUNTED_INITIALIZE) { assert(sf_base->iSessionsInitializing); sf_base->iSessionsInitializing--; @@ -218,12 +219,12 @@ void StreamUpdatePerfBaseState(SFBASE *sf_base, Flow *flow, char newState) break; case TCP_STATE_CLOSED: - if (flow->ssn_state.session_flags & SSNFLAG_COUNTED_CLOSING) + if ( session_flags & SSNFLAG_COUNTED_CLOSING ) { assert(sf_base->iSessionsClosing); sf_base->iSessionsClosing--; } - else if (flow->ssn_state.session_flags & SSNFLAG_COUNTED_ESTABLISH) + else if (session_flags & SSNFLAG_COUNTED_ESTABLISH) { assert(sf_base->iSessionsEstablished); sf_base->iSessionsEstablished--; @@ -231,7 +232,7 @@ void StreamUpdatePerfBaseState(SFBASE *sf_base, Flow *flow, char newState) if (perfmon_config && (perfmon_config->perf_flags & SFPERF_FLOWIP)) UpdateFlowIPState(&sfFlow, &flow->client_ip, &flow->server_ip, SFS_STATE_TCP_CLOSED); } - else if (flow->ssn_state.session_flags & SSNFLAG_COUNTED_INITIALIZE) + else if (session_flags & SSNFLAG_COUNTED_INITIALIZE) { assert(sf_base->iSessionsInitializing); sf_base->iSessionsInitializing--; @@ -242,6 +243,7 @@ void StreamUpdatePerfBaseState(SFBASE *sf_base, Flow *flow, char newState) break; } + flow->update_session_flags( session_flags ); sf_base->stream_mem_in_use = tcp_memcap->used(); } @@ -431,7 +433,7 @@ static void PrintTcpSession(TcpSession* ts) LogMessage(" server port: %d\n", ts->flow->server_port); LogMessage(" client port: %d\n", ts->flow->client_port); - LogMessage(" flags: 0x%X\n", ts->flow->ssn_state.session_flags); + LogMessage(" flags: 0x%X\n", ts->flow->get_session_flags()); LogMessage("Client Tracker:\n"); PrintTcpTracker(&ts->client); @@ -515,7 +517,7 @@ static inline int ValidRstSynSent(TcpTracker *st, TcpDataBlock *tdb) static inline int ValidSeq( const Packet* p, Flow* flow, TcpTracker *st, TcpDataBlock *tdb) { - uint32_t win = StreamGetWindow(flow, st, tdb); + uint32_t win = st->normalizer->get_stream_window(flow, st, tdb); if ( !p->dsize ) { @@ -538,27 +540,27 @@ static inline int ValidSeq( } #else -static inline int ValidSeq(const Packet* p, Flow* flow, TcpTracker *st, TcpDataBlock *tdb) +static inline int ValidSeq( Flow* flow, TcpTracker *st, TcpDataBlock *tdb) { int right_ok; uint32_t left_seq; DebugFormat(DEBUG_STREAM_STATE, "Checking end_seq (%X) > r_win_base (%X) && seq (%X) < r_nxt_ack(%X)\n", - tdb->end_seq, st->r_win_base, tdb->seq, st->r_nxt_ack+StreamGetWindow(flow, st, tdb)); + tdb->end_seq, st->r_win_base, tdb->seq, st->r_nxt_ack + st->normalizer->get_stream_window(flow, st, tdb)); if (SEQ_LT(st->r_nxt_ack, st->r_win_base)) left_seq = st->r_nxt_ack; else left_seq = st->r_win_base; - if (p->dsize) + if (tdb->pkt->dsize) right_ok = SEQ_GT(tdb->end_seq, left_seq); else right_ok = SEQ_GEQ(tdb->end_seq, left_seq); if (right_ok) { - uint32_t win = StreamGetWindow(flow, st, tdb); + uint32_t win = st->normalizer->get_stream_window( tdb ); if (SEQ_LEQ(tdb->seq, st->r_win_base + win)) { @@ -578,8 +580,7 @@ static inline int ValidSeq(const Packet* p, Flow* flow, TcpTracker *st, TcpDataB #endif -static inline void UpdateSsn(Packet*, TcpTracker *rcv, TcpTracker *snd, - TcpDataBlock *tdb) +static inline void UpdateSsn( TcpTracker *rcv, TcpTracker *snd, TcpDataBlock *tdb) { #if 0 if ( @@ -609,7 +610,7 @@ static inline void UpdateSsn(Packet*, TcpTracker *rcv, TcpTracker *snd, { // normalize here tdb->ack = seq; - ((TCPHdr*)p->ptrs.tcph)->th_ack = htonl(seq); + tcph->th_ack = htonl(seq); p->packet_flags |= PKT_MODIFIED; break; } @@ -645,16 +646,17 @@ static inline void UpdateSsn(Packet*, TcpTracker *rcv, TcpTracker *snd, static inline void SetupTcpDataBlock(TcpDataBlock* tdb, Packet* p) { + tdb->pkt = p; tdb->seq = ntohl(p->ptrs.tcph->th_seq); tdb->ack = ntohl(p->ptrs.tcph->th_ack); tdb->win = ntohs(p->ptrs.tcph->th_win); tdb->end_seq = tdb->seq + (uint32_t) p->dsize; tdb->ts = 0; - if (p->ptrs.tcph->th_flags & TH_SYN) + if( p->ptrs.tcph->is_syn() ) { tdb->end_seq++; - if (!(p->ptrs.tcph->th_flags & TH_ACK)) + if( !p->ptrs.tcph->is_ack() ) EventInternal(INTERNAL_EVENT_SYN_RECEIVED); } // don't bump end_seq for fin here @@ -666,8 +668,7 @@ static inline void SetupTcpDataBlock(TcpDataBlock* tdb, Packet* p) } -static void TcpSessionClear(Flow* flow, TcpSession* tcpssn, - int freeApplicationData) +static void TcpSessionClear(Flow* flow, TcpSession* tcpssn, int freeApplicationData) { DebugFormat(DEBUG_STREAM_STATE, "In TcpSessionClear, %lu bytes in use\n", tcp_memcap->used()); DebugFormat(DEBUG_STREAM_STATE, "client has %d segs queued\n", tcpssn->client.seg_count); @@ -684,16 +685,12 @@ static void TcpSessionClear(Flow* flow, TcpSession* tcpssn, StreamUpdatePerfBaseState(&sfBase, tcpssn->flow, TCP_STATE_CLOSED); RemoveStreamSession(&sfBase); - if (flow->ssn_state.session_flags & SSNFLAG_PRUNED) - { + if (flow->get_session_flags() & SSNFLAG_PRUNED) CloseStreamSession(&sfBase, SESSION_CLOSED_PRUNED); - } else if (flow->ssn_state.session_flags & SSNFLAG_TIMEDOUT) - { + else if (flow->get_session_flags() & SSNFLAG_TIMEDOUT) CloseStreamSession(&sfBase, SESSION_CLOSED_TIMEDOUT); - } else - { + else CloseStreamSession(&sfBase, SESSION_CLOSED_NORMALLY); - } tcpssn->set_splitter(true, nullptr); tcpssn->set_splitter(false, nullptr); @@ -801,30 +798,32 @@ static inline int IsWellFormed(Packet *p, TcpTracker *ts) #endif -static void FinishServerInit( Packet* p, TcpDataBlock* tdb, TcpSession* ssn ) +static void FinishServerInit( TcpDataBlock* tdb, TcpSession* ssn ) { - TcpTracker *server; - TcpTracker *client; + TcpTracker* server; + TcpTracker* client; + const tcp::TCPHdr* tcph = tdb->pkt->ptrs.tcph; - if ( !ssn ) + // FIXIT - can tcp session be null at this point? + if( !ssn ) return; server = &ssn->server; client = &ssn->client; - server->l_window = tdb->win; /* set initial server window */ + server->l_window = tdb->win; server->l_unackd = tdb->seq + 1; server->l_nxt_seq = server->l_unackd; server->isn = tdb->seq; client->r_nxt_ack = tdb->end_seq; - if ( p->ptrs.tcph->th_flags & TH_FIN ) + if( tcph->is_fin() ) server->l_nxt_seq--; DebugFormat(DEBUG_STREAM_STATE, "seglist_base_seq = %X\n", client->seglist_base_seq); - if ( !( ssn->flow->session_state & STREAM_STATE_MIDSTREAM ) ) + if( !( ssn->flow->session_state & STREAM_STATE_MIDSTREAM ) ) { server->s_mgr.state = TCP_STATE_SYN_RCVD; client->seglist_base_seq = server->l_unackd; @@ -836,14 +835,15 @@ static void FinishServerInit( Packet* p, TcpDataBlock* tdb, TcpSession* ssn ) client->r_win_base = tdb->seq; } - server->flags |= StreamGetTcpTimestamp(p, &server->ts_last, 0); + server->flags |= server->normalizer->get_tcp_timestamp(tdb, false); + server->ts_last = tdb->ts; if (server->ts_last == 0) server->flags |= TF_TSTAMP_ZERO; else - server->ts_last_pkt = p->pkth->ts.tv_sec; + server->ts_last_pkt = tdb->pkt->pkth->ts.tv_sec; - server->flags |= StreamGetMss(p, &server->mss); - server->flags |= StreamGetWscale(p, &server->wscale); + server->flags |= StreamGetMss(tdb->pkt, &server->mss); + server->flags |= StreamGetWscale(tdb->pkt, &server->wscale); #ifdef DEBUG_STREAM_EX PrintTcpSession(ssn); @@ -855,8 +855,10 @@ static inline void EndOfFileHandle(Packet* p, TcpSession* tcpssn) tcpssn->flow->call_handlers(p, true); } -static void NewQueue(TcpTracker *st, Packet *p, TcpDataBlock *tdb) +static void NewQueue(TcpTracker *st, TcpDataBlock *tdb) { + const tcp::TCPHdr* tcph = tdb->pkt->ptrs.tcph; + PROFILE_VARS; MODULE_PROFILE_START(s5TcpInsertPerfStats); DebugMessage(DEBUG_STREAM_STATE, "In NewQueue\n"); @@ -864,7 +866,7 @@ static void NewQueue(TcpTracker *st, Packet *p, TcpDataBlock *tdb) uint32_t overlap = 0; uint32_t seq = tdb->seq; - if (p->ptrs.tcph->th_flags & TH_SYN) + if( tcph->is_syn() ) seq++; /* new packet seq is below the last ack... */ @@ -873,7 +875,7 @@ static void NewQueue(TcpTracker *st, Packet *p, TcpDataBlock *tdb) DebugMessage(DEBUG_STREAM_STATE, "segment overlaps ack'd data...\n"); overlap = st->r_win_base - tdb->seq; - if (overlap >= p->dsize) + if (overlap >= tdb->pkt->dsize) { DebugMessage(DEBUG_STREAM_STATE, "full overlap on ack'd data, dropping segment\n"); MODULE_PROFILE_END(s5TcpInsertPerfStats); @@ -882,7 +884,7 @@ static void NewQueue(TcpTracker *st, Packet *p, TcpDataBlock *tdb) } // BLOCK add new block to seglist containing data - AddStreamNode(st, p, tdb, p->dsize, overlap, 0, tdb->seq + overlap, NULL); + AddStreamNode(st, tdb, tdb->pkt->dsize, overlap, 0, tdb->seq + overlap, NULL); DebugFormat(DEBUG_STREAM_STATE, "Attached new queue to seglist, %d bytes queued, base_seq 0x%X\n", p->dsize-overlap, st->seglist_base_seq); @@ -891,12 +893,12 @@ static void NewQueue(TcpTracker *st, Packet *p, TcpDataBlock *tdb) } -static void ProcessTcpStream(TcpTracker *rcv, TcpSession *tcpssn, Packet *p, - TcpDataBlock *tdb, StreamTcpConfig* config) +static void ProcessTcpStream(TcpTracker *rcv, TcpSession *tcpssn, TcpDataBlock *tdb, + StreamTcpConfig* config) { DebugFormat(DEBUG_STREAM_STATE, "In ProcessTcpStream(), %d bytes to queue\n", p->dsize); - if (p->packet_flags & PKT_IGNORE) + if (tdb->pkt->packet_flags & PKT_IGNORE) return; #ifdef HAVE_DAQ_ADDRESS_SPACE_ID @@ -917,7 +919,7 @@ static void ProcessTcpStream(TcpTracker *rcv, TcpSession *tcpssn, Packet *p, if (config->max_consec_small_segs) { - if (p->dsize < config->max_consec_small_seg_size) + if (tdb->pkt->dsize < config->max_consec_small_seg_size) { rcv->small_seg_count++; @@ -950,27 +952,27 @@ static void ProcessTcpStream(TcpTracker *rcv, TcpSession *tcpssn, Packet *p, if (!rcv->seg_count) { - NewQueue(rcv, p, tdb); + NewQueue(rcv, tdb); return; } if (SEQ_GT(rcv->r_win_base, tdb->seq)) { uint32_t offset = rcv->r_win_base - tdb->seq; - if (offset < p->dsize) + if (offset < tdb->pkt->dsize) { tdb->seq += offset; - p->data += offset; - p->dsize -= (uint16_t) offset; + tdb->pkt->data += offset; + tdb->pkt->dsize -= (uint16_t) offset; - StreamQueue(rcv, p, tdb, tcpssn); + StreamQueue(rcv, tdb, tcpssn); - p->dsize += (uint16_t) offset; - p->data -= offset; + tdb->pkt->dsize += (uint16_t) offset; + tdb->pkt->data -= offset; tdb->seq -= offset; } } else - StreamQueue(rcv, p, tdb, tcpssn); + StreamQueue(rcv, tdb, tcpssn); if ((rcv->config->overlap_limit) && (rcv->overlap_count > rcv->config->overlap_limit)) @@ -981,14 +983,16 @@ static void ProcessTcpStream(TcpTracker *rcv, TcpSession *tcpssn, Packet *p, } } -static int ProcessTcpData(Packet *p, TcpTracker *listener, TcpSession *tcpssn, +static int ProcessTcpData(TcpTracker *listener, TcpSession *tcpssn, TcpDataBlock *tdb, StreamTcpConfig *config) { + const tcp::TCPHdr* tcph = tdb->pkt->ptrs.tcph; + PROFILE_VARS; MODULE_PROFILE_START(s5TcpDataPerfStats); uint32_t seq = tdb->seq; - if (p->ptrs.tcph->th_flags & TH_SYN) + if( tcph->is_syn() ) { if (listener->os_policy == STREAM_POLICY_MACOS) seq++; @@ -996,7 +1000,7 @@ static int ProcessTcpData(Packet *p, TcpTracker *listener, TcpSession *tcpssn, else { DebugMessage(DEBUG_STREAM_STATE, "Bailing, data on SYN, not MAC Policy!\n"); - NormalTrimPayloadIfSyn(p, 0, tdb); + listener->normalizer->trim_syn_payload( tdb ); MODULE_PROFILE_END(s5TcpDataPerfStats); return STREAM_UNALIGNED; } @@ -1007,10 +1011,10 @@ static int ProcessTcpData(Packet *p, TcpTracker *listener, TcpSession *tcpssn, { /* check if we're in the window */ if (listener->config->policy != STREAM_POLICY_PROXY - and StreamGetWindow(tcpssn->flow, listener, tdb) == 0) + and listener->normalizer->get_stream_window( tdb ) == 0) { DebugMessage(DEBUG_STREAM_STATE, "Bailing, we're out of the window!\n"); - NormalTrimPayloadIfWin(p, 0, tdb); + listener->normalizer->trim_win_payload( tdb ); MODULE_PROFILE_END(s5TcpDataPerfStats); return STREAM_UNALIGNED; } @@ -1020,12 +1024,12 @@ static int ProcessTcpData(Packet *p, TcpTracker *listener, TcpSession *tcpssn, if (listener->s_mgr.state_queue == TCP_STATE_NONE) listener->r_nxt_ack = tdb->end_seq; - if (p->dsize != 0) + if (tdb->pkt->dsize != 0) { - if (!(tcpssn->flow->ssn_state.session_flags & SSNFLAG_STREAM_ORDER_BAD)) - p->packet_flags |= PKT_STREAM_ORDER_OK; + if (!(tcpssn->flow->get_session_flags() & SSNFLAG_STREAM_ORDER_BAD)) + tdb->pkt->packet_flags |= PKT_STREAM_ORDER_OK; - ProcessTcpStream(listener, tcpssn, p, tdb, config); + ProcessTcpStream(listener, tcpssn, tdb, config); /* set flags to session flags */ MODULE_PROFILE_END(s5TcpDataPerfStats); @@ -1051,10 +1055,10 @@ static int ProcessTcpData(Packet *p, TcpTracker *listener, TcpSession *tcpssn, { /* check if we're in the window */ if (listener->config->policy != STREAM_POLICY_PROXY - and StreamGetWindow(tcpssn->flow, listener, tdb) == 0) + and listener->normalizer->get_stream_window( tdb ) == 0) { DebugMessage(DEBUG_STREAM_STATE, "Bailing, we're out of the window!\n"); - NormalTrimPayloadIfWin(p, 0, tdb); + listener->normalizer->trim_win_payload( tdb ); MODULE_PROFILE_END(s5TcpDataPerfStats); return STREAM_UNALIGNED; } @@ -1072,14 +1076,14 @@ static int ProcessTcpData(Packet *p, TcpTracker *listener, TcpSession *tcpssn, } } - if (p->dsize != 0) + if (tdb->pkt->dsize != 0) { - if (!(tcpssn->flow->ssn_state.session_flags & SSNFLAG_STREAM_ORDER_BAD)) + if (!(tcpssn->flow->get_session_flags() & SSNFLAG_STREAM_ORDER_BAD)) { - if (!SEQ_LEQ((tdb->seq + p->dsize), listener->r_nxt_ack)) - tcpssn->flow->ssn_state.session_flags |= SSNFLAG_STREAM_ORDER_BAD; + if (!SEQ_LEQ((tdb->seq + tdb->pkt->dsize), listener->r_nxt_ack)) + tcpssn->flow->set_session_flags( SSNFLAG_STREAM_ORDER_BAD ); } - ProcessTcpStream(listener, tcpssn, p, tdb, config); + ProcessTcpStream(listener, tcpssn, tdb, config); } } @@ -1097,12 +1101,16 @@ static void SetOSPolicy(Flow* flow, TcpSession* tcpssn) if (!tcpssn->client.os_policy) { tcpssn->client.os_policy = flow->ssn_policy ? flow->ssn_policy : tcpssn->client.config->policy; + tcpssn->client.normalizer = TcpNormalizerFactory::allocate_normalizer( tcpssn->client.os_policy, + tcpssn, &tcpssn->client, &tcpssn->server ); SetTcpReassemblyPolicy(&tcpssn->client); } if (!tcpssn->server.os_policy) { tcpssn->server.os_policy = flow->ssn_policy ? flow->ssn_policy : tcpssn->server.config->policy; + tcpssn->server.normalizer = TcpNormalizerFactory::allocate_normalizer( tcpssn->client.os_policy, + tcpssn, &tcpssn->server, &tcpssn->client ); SetTcpReassemblyPolicy(&tcpssn->server); } } @@ -1190,7 +1198,7 @@ static inline void CopyMacAddr(Packet* p, TcpSession* tcpssn, int dir) } } -static void NewTcpSession(Packet* p, Flow* flow, StreamTcpConfig* dstPolicy, TcpSession* tmp) +static void NewTcpSession(Packet* p, Flow* flow, StreamTcpConfig* dstPolicy, TcpSession* tss) { Inspector* ins = flow->gadget; @@ -1201,7 +1209,8 @@ static void NewTcpSession(Packet* p, Flow* flow, StreamTcpConfig* dstPolicy, Tcp { stream.set_splitter(flow, true, ins->get_splitter(true)); stream.set_splitter(flow, false, ins->get_splitter(false)); - } else + } + else { stream.set_splitter(flow, true, new AtomSplitter(true)); stream.set_splitter(flow, false, new AtomSplitter(false)); @@ -1210,24 +1219,18 @@ static void NewTcpSession(Packet* p, Flow* flow, StreamTcpConfig* dstPolicy, Tcp { DebugMessage(DEBUG_STREAM_STATE, "adding TcpSession to lightweight session\n"); flow->protocol = p->type(); - tmp->flow = flow; - - /* New session, previous was marked as reset. Clear the - * reset flag. */ - if (flow->ssn_state.session_flags & SSNFLAG_RESET) - flow->ssn_state.session_flags &= ~SSNFLAG_RESET; + tss->flow = flow; - SetOSPolicy(flow, tmp); - - if ((flow->ssn_state.session_flags & SSNFLAG_CLIENT_SWAP) - && !(flow->ssn_state.session_flags & SSNFLAG_CLIENT_SWAPPED)) + /* New session, previous was marked as reset. Clear the reset flag. */ + uint32_t session_flags = flow->clear_session_flags( SSNFLAG_RESET ); + if ((session_flags & SSNFLAG_CLIENT_SWAP) && !(session_flags & SSNFLAG_CLIENT_SWAPPED)) { - TcpTracker trk = tmp->client; + TcpTracker trk = tss->client; sfip_t ip = flow->client_ip; uint16_t port = flow->client_port; - tmp->client = tmp->server; - tmp->server = trk; + tss->client = tss->server; + tss->server = trk; flow->client_ip = flow->server_ip; flow->server_ip = ip; @@ -1237,317 +1240,317 @@ static void NewTcpSession(Packet* p, Flow* flow, StreamTcpConfig* dstPolicy, Tcp if (!flow->two_way_traffic()) { - if (flow->ssn_state.session_flags & SSNFLAG_SEEN_CLIENT) + if (session_flags & SSNFLAG_SEEN_CLIENT) { - flow->ssn_state.session_flags ^= SSNFLAG_SEEN_CLIENT; - flow->ssn_state.session_flags |= SSNFLAG_SEEN_SERVER; + session_flags ^= SSNFLAG_SEEN_CLIENT; + session_flags |= SSNFLAG_SEEN_SERVER; } - else if (flow->ssn_state.session_flags & SSNFLAG_SEEN_SERVER) + else if (session_flags & SSNFLAG_SEEN_SERVER) { - flow->ssn_state.session_flags ^= SSNFLAG_SEEN_SERVER; - flow->ssn_state.session_flags |= SSNFLAG_SEEN_CLIENT; + session_flags ^= SSNFLAG_SEEN_SERVER; + session_flags |= SSNFLAG_SEEN_CLIENT; } } - flow->ssn_state.session_flags |= SSNFLAG_CLIENT_SWAPPED; + + session_flags |= SSNFLAG_CLIENT_SWAPPED; + flow->update_session_flags( session_flags ); } - init_flush_policy(flow, &tmp->server); - init_flush_policy(flow, &tmp->client); + init_flush_policy(flow, &tss->server); + init_flush_policy(flow, &tss->client); #ifdef DEBUG_STREAM_EX - PrintTcpSession(tmp); + PrintTcpSession(tss); #endif flow->set_expire(p, dstPolicy->session_timeout); AddStreamSession(&sfBase, - flow->session_state & STREAM_STATE_MIDSTREAM ? - SSNFLAG_MIDSTREAM : 0); + flow->session_state & STREAM_STATE_MIDSTREAM ? SSNFLAG_MIDSTREAM : 0); - StreamUpdatePerfBaseState(&sfBase, tmp->flow, TCP_STATE_SYN_SENT); + StreamUpdatePerfBaseState(&sfBase, tss->flow, TCP_STATE_SYN_SENT); EventInternal(INTERNAL_EVENT_SESSION_ADD); - tmp->ecn = 0; - assert(!tmp->tcp_init); - tmp->tcp_init = true; + tss->ecn = 0; + assert(!tss->tcp_init); + tss->tcp_init = true; tcpStats.trackers_created++; } } -static void NewTcpSessionOnSyn(Packet* p, Flow* flow, TcpDataBlock* tdb, - StreamTcpConfig* dstPolicy) +static void NewTcpSessionOnSyn(Flow* flow, TcpDataBlock* tdb, StreamTcpConfig* dstPolicy) { + const tcp::TCPHdr* tcph = tdb->pkt->ptrs.tcph; + PROFILE_VARS; MODULE_PROFILE_START(s5TcpNewSessPerfStats); - TcpSession* tmp; - { - /****************************************************************** - * start new sessions on proper SYN packets - *****************************************************************/ - tmp = (TcpSession*) flow->session; - DebugMessage(DEBUG_STREAM_STATE, "Creating new session tracker on SYN!\n"); + TcpSession* tss; - flow->ssn_state.session_flags |= SSNFLAG_SEEN_CLIENT; + /****************************************************************** + * start new sessions on proper SYN packets + *****************************************************************/ + tss = (TcpSession*) flow->session; + DebugMessage(DEBUG_STREAM_STATE, "Creating new session tracker on SYN!\n"); - if (p->ptrs.tcph->are_flags_set(TH_CWR | TH_ECE)) - { - flow->ssn_state.session_flags |= SSNFLAG_ECN_CLIENT_QUERY; - } + flow->set_session_flags( SSNFLAG_SEEN_CLIENT ); - /* setup the stream trackers */ - tmp->client.s_mgr.state = TCP_STATE_SYN_SENT; - tmp->client.isn = tdb->seq; - tmp->client.l_unackd = tdb->seq + 1; - tmp->client.l_nxt_seq = tmp->client.l_unackd; + if (tcph->are_flags_set(TH_CWR | TH_ECE)) + flow->set_session_flags( SSNFLAG_ECN_CLIENT_QUERY ); - if (tdb->seq != tdb->end_seq) - tmp->client.l_nxt_seq += (tdb->end_seq - tdb->seq - 1); + /* setup the stream trackers */ + /* Set the StreamTcpConfig for each direction (pkt from client) */ + tss->client.config = dstPolicy; // FIXIT-M use external binding for both dirs + tss->server.config = dstPolicy; // (applies to all the blocks in this funk) + SetOSPolicy(flow, tss); - tmp->client.l_window = tdb->win; - tmp->client.ts_last_pkt = p->pkth->ts.tv_sec; + tss->client.s_mgr.state = TCP_STATE_SYN_SENT; + tss->client.isn = tdb->seq; + tss->client.l_unackd = tdb->seq + 1; + tss->client.l_nxt_seq = tss->client.l_unackd; - tmp->server.seglist_base_seq = tmp->client.l_unackd; - tmp->server.r_nxt_ack = tmp->client.l_unackd; - tmp->server.r_win_base = tdb->seq + 1; + if (tdb->seq != tdb->end_seq) + tss->client.l_nxt_seq += (tdb->end_seq - tdb->seq - 1); - DebugFormat(DEBUG_STREAM_STATE, "seglist_base_seq = %X\n", tmp->server.seglist_base_seq); - tmp->server.s_mgr.state = TCP_STATE_LISTEN; + tss->client.l_window = tdb->win; + tss->client.ts_last_pkt = tdb->pkt->pkth->ts.tv_sec; - tmp->client.flags |= StreamGetTcpTimestamp(p, &tmp->client.ts_last, 0); - if (tmp->client.ts_last == 0) - tmp->client.flags |= TF_TSTAMP_ZERO; - tmp->client.flags |= StreamGetMss(p, &tmp->client.mss); - tmp->client.flags |= StreamGetWscale(p, &tmp->client.wscale); + tss->server.seglist_base_seq = tss->client.l_unackd; + tss->server.r_nxt_ack = tss->client.l_unackd; + tss->server.r_win_base = tdb->seq + 1; - /* Set the StreamTcpConfig for each direction (pkt from client) */ - tmp->client.config = dstPolicy; // FIXIT-M use external binding for both dirs - tmp->server.config = dstPolicy; // (applies to all the blocks in this funk) + DebugFormat(DEBUG_STREAM_STATE, "seglist_base_seq = %X\n", tss->server.seglist_base_seq); + tss->server.s_mgr.state = TCP_STATE_LISTEN; + + tss->client.flags |= tss->client.normalizer->get_tcp_timestamp(tdb, false); + tss->client.ts_last = tdb->ts; + if (tss->client.ts_last == 0) + tss->client.flags |= TF_TSTAMP_ZERO; + tss->client.flags |= StreamGetMss(tdb->pkt, &tss->client.mss); + tss->client.flags |= StreamGetWscale(tdb->pkt, &tss->client.wscale); + + CopyMacAddr(tdb->pkt, tss, FROM_CLIENT); - CopyMacAddr(p, tmp, FROM_CLIENT); - } tcpStats.sessions_on_syn++; - NewTcpSession(p, flow, dstPolicy, tmp); + NewTcpSession(tdb->pkt, flow, dstPolicy, tss); MODULE_PROFILE_END(s5TcpNewSessPerfStats); } -static void NewTcpSessionOnSynAck(Packet* p, Flow* flow, TcpDataBlock* tdb, - StreamTcpConfig* dstPolicy) +static void NewTcpSessionOnSynAck(Flow* flow, TcpDataBlock* tdb, StreamTcpConfig* dstPolicy) { - PROFILE_VARS; MODULE_PROFILE_START(s5TcpNewSessPerfStats); - TcpSession* tmp; - { - tmp = (TcpSession*) flow->session; - DebugMessage(DEBUG_STREAM_STATE, "Creating new session tracker on SYN_ACK!\n"); + const tcp::TCPHdr* tcph = tdb->pkt->ptrs.tcph; - flow->ssn_state.session_flags |= SSNFLAG_SEEN_SERVER; - - if (p->ptrs.tcph->are_flags_set(TH_CWR | TH_ECE)) - { - flow->ssn_state.session_flags |= SSNFLAG_ECN_SERVER_REPLY; - } + PROFILE_VARS; MODULE_PROFILE_START(s5TcpNewSessPerfStats); + TcpSession* tss; + + tss = (TcpSession*) flow->session; + DebugMessage(DEBUG_STREAM_STATE, "Creating new session tracker on SYN_ACK!\n"); + + flow->set_session_flags( SSNFLAG_SEEN_SERVER ); + if (tcph->are_flags_set(TH_CWR | TH_ECE)) + flow->set_session_flags( SSNFLAG_ECN_SERVER_REPLY ); + + /* setup the stream trackers */ + /* Set the config for each direction (pkt from server) */ + tss->server.config = dstPolicy; + tss->client.config = dstPolicy; + SetOSPolicy(flow, tss); + + tss->server.s_mgr.state = TCP_STATE_SYN_RCVD; + tss->server.isn = tdb->seq; + tss->server.l_unackd = tdb->seq + 1; + tss->server.l_nxt_seq = tss->server.l_unackd; + tss->server.l_window = tdb->win; + + tss->server.seglist_base_seq = tdb->ack; + tss->server.r_win_base = tdb->ack; + tss->server.r_nxt_ack = tdb->ack; + tss->server.ts_last_pkt = tdb->pkt->pkth->ts.tv_sec; + + tss->client.seglist_base_seq = tss->server.l_unackd; + tss->client.r_nxt_ack = tss->server.l_unackd; + tss->client.r_win_base = tdb->seq + 1; + tss->client.l_nxt_seq = tdb->ack; + tss->client.isn = tdb->ack - 1; + + DebugFormat(DEBUG_STREAM_STATE, "seglist_base_seq = %X\n", tss->client.seglist_base_seq); + tss->client.s_mgr.state = TCP_STATE_SYN_SENT; + + tss->server.flags |= tss->server.normalizer->get_tcp_timestamp(tdb, false); + tss->server.ts_last = tdb->ts; + if (tss->server.ts_last == 0) + tss->server.flags |= TF_TSTAMP_ZERO; + tss->server.flags |= StreamGetMss(tdb->pkt, &tss->server.mss); + tss->server.flags |= StreamGetWscale(tdb->pkt, &tss->server.wscale); + + CopyMacAddr(tdb->pkt, tss, FROM_SERVER); - /* setup the stream trackers */ - tmp->server.s_mgr.state = TCP_STATE_SYN_RCVD; - tmp->server.isn = tdb->seq; - tmp->server.l_unackd = tdb->seq + 1; - tmp->server.l_nxt_seq = tmp->server.l_unackd; - tmp->server.l_window = tdb->win; - - tmp->server.seglist_base_seq = tdb->ack; - tmp->server.r_win_base = tdb->ack; - tmp->server.r_nxt_ack = tdb->ack; - tmp->server.ts_last_pkt = p->pkth->ts.tv_sec; - - tmp->client.seglist_base_seq = tmp->server.l_unackd; - tmp->client.r_nxt_ack = tmp->server.l_unackd; - tmp->client.r_win_base = tdb->seq + 1; - tmp->client.l_nxt_seq = tdb->ack; - tmp->client.isn = tdb->ack - 1; - - DebugFormat(DEBUG_STREAM_STATE, "seglist_base_seq = %X\n", tmp->client.seglist_base_seq); - tmp->client.s_mgr.state = TCP_STATE_SYN_SENT; - - tmp->server.flags |= StreamGetTcpTimestamp(p, &tmp->server.ts_last, 0); - if (tmp->server.ts_last == 0) - tmp->server.flags |= TF_TSTAMP_ZERO; - tmp->server.flags |= StreamGetMss(p, &tmp->server.mss); - tmp->server.flags |= StreamGetWscale(p, &tmp->server.wscale); - - /* Set the config for each direction (pkt from server) */ - tmp->server.config = dstPolicy; - tmp->client.config = dstPolicy; - - CopyMacAddr(p, tmp, FROM_SERVER); - } tcpStats.sessions_on_syn_ack++; - NewTcpSession(p, flow, dstPolicy, tmp); + NewTcpSession(tdb->pkt, flow, dstPolicy, tss); MODULE_PROFILE_END(s5TcpNewSessPerfStats); } -static void NewTcpSessionOn3Way(Packet* p, Flow* flow, TcpDataBlock* tdb, +static void NewTcpSessionOn3Way(Flow* flow, TcpDataBlock* tdb, StreamTcpConfig* dstPolicy) { + const tcp::TCPHdr* tcph = tdb->pkt->ptrs.tcph; + PROFILE_VARS; MODULE_PROFILE_START(s5TcpNewSessPerfStats); - TcpSession* tmp; - { - /****************************************************************** - * start new sessions on completion of 3-way (ACK only, no data) - *****************************************************************/ - tmp = (TcpSession*) flow->session; - DebugMessage(DEBUG_STREAM_STATE, "Creating new session tracker on ACK!\n"); + TcpSession* tss; - flow->ssn_state.session_flags |= SSNFLAG_SEEN_CLIENT; + /****************************************************************** + * start new sessions on completion of 3-way (ACK only, no data) + *****************************************************************/ + tss = (TcpSession*) flow->session; + DebugMessage(DEBUG_STREAM_STATE, "Creating new session tracker on ACK!\n"); - if (p->ptrs.tcph->are_flags_set(TH_CWR | TH_ECE)) - { - flow->ssn_state.session_flags |= SSNFLAG_ECN_CLIENT_QUERY; - } + flow->set_session_flags( SSNFLAG_SEEN_CLIENT ); - /* setup the stream trackers */ - tmp->client.s_mgr.state = TCP_STATE_ESTABLISHED; - tmp->client.isn = tdb->seq; - tmp->client.l_unackd = tdb->seq + 1; - tmp->client.l_nxt_seq = tmp->client.l_unackd; - tmp->client.l_window = tdb->win; + if (tcph->are_flags_set(TH_CWR | TH_ECE)) + flow->set_session_flags( SSNFLAG_ECN_CLIENT_QUERY ); - tmp->client.ts_last_pkt = p->pkth->ts.tv_sec; + /* setup the stream trackers */ + /* Set the config for each direction (pkt from client) */ + tss->client.config = dstPolicy; + tss->server.config = dstPolicy; + SetOSPolicy(flow, tss); - tmp->server.seglist_base_seq = tmp->client.l_unackd; - tmp->server.r_nxt_ack = tmp->client.l_unackd; - tmp->server.r_win_base = tdb->seq + 1; + tss->client.s_mgr.state = TCP_STATE_ESTABLISHED; + tss->client.isn = tdb->seq; + tss->client.l_unackd = tdb->seq + 1; + tss->client.l_nxt_seq = tss->client.l_unackd; + tss->client.l_window = tdb->win; - DebugFormat(DEBUG_STREAM_STATE, "seglist_base_seq = %X\n", tmp->server.seglist_base_seq); - tmp->server.s_mgr.state = TCP_STATE_ESTABLISHED; + tss->client.ts_last_pkt = tdb->pkt->pkth->ts.tv_sec; - tmp->client.flags |= StreamGetTcpTimestamp(p, &tmp->client.ts_last, 0); - if (tmp->client.ts_last == 0) - tmp->client.flags |= TF_TSTAMP_ZERO; - tmp->client.flags |= StreamGetMss(p, &tmp->client.mss); - tmp->client.flags |= StreamGetWscale(p, &tmp->client.wscale); + tss->server.seglist_base_seq = tss->client.l_unackd; + tss->server.r_nxt_ack = tss->client.l_unackd; + tss->server.r_win_base = tdb->seq + 1; - /* Set the config for each direction (pkt from client) */ - tmp->client.config = dstPolicy; - tmp->server.config = dstPolicy; + DebugFormat(DEBUG_STREAM_STATE, "seglist_base_seq = %X\n", tss->server.seglist_base_seq); + tss->server.s_mgr.state = TCP_STATE_ESTABLISHED; + + tss->client.flags |= tss->client.normalizer->get_tcp_timestamp(tdb, false); + tss->client.ts_last = tdb->ts; + if (tss->client.ts_last == 0) + tss->client.flags |= TF_TSTAMP_ZERO; + tss->client.flags |= StreamGetMss(tdb->pkt, &tss->client.mss); + tss->client.flags |= StreamGetWscale(tdb->pkt, &tss->client.wscale); + + CopyMacAddr(tdb->pkt, tss, FROM_CLIENT); - CopyMacAddr(p, tmp, FROM_CLIENT); - } tcpStats.sessions_on_3way++; - NewTcpSession(p, flow, dstPolicy, tmp); + NewTcpSession(tdb->pkt, flow, dstPolicy, tss); MODULE_PROFILE_END(s5TcpNewSessPerfStats); } -static void NewTcpSessionOnData(Packet* p, Flow* flow, TcpDataBlock* tdb, - StreamTcpConfig* dstPolicy) +static void NewTcpSessionOnData(Flow* flow, TcpDataBlock* tdb, StreamTcpConfig* dstPolicy) { - PROFILE_VARS; MODULE_PROFILE_START(s5TcpNewSessPerfStats); - TcpSession* tmp; - { - tmp = (TcpSession*) flow->session; - DebugMessage(DEBUG_STREAM_STATE, "Creating new session tracker on data packet (ACK|PSH)!\n"); + const tcp::TCPHdr* tcph = tdb->pkt->ptrs.tcph; - if (flow->ssn_state.direction == FROM_CLIENT) - { - DebugMessage(DEBUG_STREAM_STATE, "Session direction is FROM_CLIENT\n"); - - /* Sender is client (src port is higher) */ - flow->ssn_state.session_flags |= SSNFLAG_SEEN_CLIENT; + PROFILE_VARS; MODULE_PROFILE_START(s5TcpNewSessPerfStats); + TcpSession* tss; - if (p->ptrs.tcph->are_flags_set(TH_CWR | TH_ECE)) - flow->ssn_state.session_flags |= SSNFLAG_ECN_CLIENT_QUERY; + tss = (TcpSession*) flow->session; + DebugMessage(DEBUG_STREAM_STATE, "Creating new session tracker on data packet (ACK|PSH)!\n"); - /* setup the stream trackers */ - tmp->client.s_mgr.state = TCP_STATE_ESTABLISHED; - tmp->client.isn = tdb->seq; - tmp->client.l_unackd = tdb->seq; - tmp->client.l_nxt_seq = tmp->client.l_unackd; - tmp->client.l_window = tdb->win; + /* Set the config for each direction (pkt from client) */ + tss->client.config = dstPolicy; + tss->server.config = dstPolicy; + SetOSPolicy(flow, tss); + if (flow->ssn_state.direction == FROM_CLIENT) + { + DebugMessage(DEBUG_STREAM_STATE, "Session direction is FROM_CLIENT\n"); - tmp->client.ts_last_pkt = p->pkth->ts.tv_sec; + /* Sender is client (src port is higher) */ + flow->set_session_flags( SSNFLAG_SEEN_CLIENT ); + if (tcph->are_flags_set(TH_CWR | TH_ECE)) + flow->set_session_flags( SSNFLAG_ECN_CLIENT_QUERY ); - tmp->server.seglist_base_seq = tmp->client.l_unackd; - tmp->server.r_nxt_ack = tmp->client.l_unackd; - tmp->server.r_win_base = tdb->seq; - tmp->server.l_window = 0; /* reset later */ + /* setup the stream trackers */ + tss->client.s_mgr.state = TCP_STATE_ESTABLISHED; + tss->client.isn = tdb->seq; + tss->client.l_unackd = tdb->seq; + tss->client.l_nxt_seq = tss->client.l_unackd; + tss->client.l_window = tdb->win; - /* Next server packet is what was ACKd */ - //tmp->server.l_nxt_seq = tdb->ack + 1; - tmp->server.l_unackd = tdb->ack - 1; + tss->client.ts_last_pkt = tdb->pkt->pkth->ts.tv_sec; - DebugFormat(DEBUG_STREAM_STATE, "seglist_base_seq = %X\n", tmp->server.seglist_base_seq); - tmp->server.s_mgr.state = TCP_STATE_ESTABLISHED; + tss->server.seglist_base_seq = tss->client.l_unackd; + tss->server.r_nxt_ack = tss->client.l_unackd; + tss->server.r_win_base = tdb->seq; + tss->server.l_window = 0; /* reset later */ - tmp->client.flags |= StreamGetTcpTimestamp(p, &tmp->client.ts_last, - 0); - if (tmp->client.ts_last == 0) - tmp->client.flags |= TF_TSTAMP_ZERO; + /* Next server packet is what was ACKd */ + //tss->server.l_nxt_seq = tdb->ack + 1; + tss->server.l_unackd = tdb->ack - 1; - tmp->client.flags |= StreamGetMss(p, &tmp->client.mss); - tmp->client.flags |= StreamGetWscale(p, &tmp->client.wscale); + DebugFormat(DEBUG_STREAM_STATE, "seglist_base_seq = %X\n", tss->server.seglist_base_seq); + tss->server.s_mgr.state = TCP_STATE_ESTABLISHED; - /* Set the config for each direction (pkt from client) */ - tmp->client.config = dstPolicy; - tmp->server.config = dstPolicy; + tss->client.flags |= tss->client.normalizer->get_tcp_timestamp(tdb, false); + tss->client.ts_last = tdb->ts; + if (tss->client.ts_last == 0) + tss->client.flags |= TF_TSTAMP_ZERO; - CopyMacAddr(p, tmp, FROM_CLIENT); - } - else - { - DebugMessage(DEBUG_STREAM_STATE, "Session direction is FROM_SERVER\n"); + tss->client.flags |= StreamGetMss(tdb->pkt, &tss->client.mss); + tss->client.flags |= StreamGetWscale(tdb->pkt, &tss->client.wscale); - /* Sender is server (src port is lower) */ - flow->ssn_state.session_flags |= SSNFLAG_SEEN_SERVER; + CopyMacAddr(tdb->pkt, tss, FROM_CLIENT); + } + else + { + DebugMessage(DEBUG_STREAM_STATE, "Session direction is FROM_SERVER\n"); - /* setup the stream trackers */ - tmp->server.s_mgr.state = TCP_STATE_ESTABLISHED; - tmp->server.isn = tdb->seq; - tmp->server.l_unackd = tdb->seq; - tmp->server.l_nxt_seq = tmp->server.l_unackd; - tmp->server.l_window = tdb->win; + /* Sender is server (src port is lower) */ + flow->set_session_flags( SSNFLAG_SEEN_SERVER ); - tmp->server.seglist_base_seq = tdb->ack; - tmp->server.r_win_base = tdb->ack; - tmp->server.r_nxt_ack = tdb->ack; - tmp->server.ts_last_pkt = p->pkth->ts.tv_sec; + /* setup the stream trackers */ + tss->server.s_mgr.state = TCP_STATE_ESTABLISHED; + tss->server.isn = tdb->seq; + tss->server.l_unackd = tdb->seq; + tss->server.l_nxt_seq = tss->server.l_unackd; + tss->server.l_window = tdb->win; - tmp->client.seglist_base_seq = tmp->server.l_unackd; - tmp->client.r_nxt_ack = tmp->server.l_unackd; - tmp->client.r_win_base = tdb->seq; - tmp->client.l_window = 0; /* reset later */ - tmp->client.isn = tdb->ack - 1; + tss->server.seglist_base_seq = tdb->ack; + tss->server.r_win_base = tdb->ack; + tss->server.r_nxt_ack = tdb->ack; + tss->server.ts_last_pkt = tdb->pkt->pkth->ts.tv_sec; - DebugFormat(DEBUG_STREAM_STATE, "seglist_base_seq = %X\n", tmp->client.seglist_base_seq); - tmp->client.s_mgr.state = TCP_STATE_ESTABLISHED; + tss->client.seglist_base_seq = tss->server.l_unackd; + tss->client.r_nxt_ack = tss->server.l_unackd; + tss->client.r_win_base = tdb->seq; + tss->client.l_window = 0; /* reset later */ + tss->client.isn = tdb->ack - 1; - tmp->server.flags |= StreamGetTcpTimestamp(p, &tmp->server.ts_last, - 0); - if (tmp->server.ts_last == 0) - tmp->server.flags |= TF_TSTAMP_ZERO; + DebugFormat(DEBUG_STREAM_STATE, "seglist_base_seq = %X\n", tss->client.seglist_base_seq); + tss->client.s_mgr.state = TCP_STATE_ESTABLISHED; - tmp->server.flags |= StreamGetMss(p, &tmp->server.mss); - tmp->server.flags |= StreamGetWscale(p, &tmp->server.wscale); + tss->server.flags |= tss->server.normalizer->get_tcp_timestamp(tdb, 0); + tss->server.ts_last = tdb->ts; + if (tss->server.ts_last == 0) + tss->server.flags |= TF_TSTAMP_ZERO; - /* Set the config for each direction (pkt from server) */ - tmp->server.config = dstPolicy; - tmp->client.config = dstPolicy; + tss->server.flags |= StreamGetMss(tdb->pkt, &tss->server.mss); + tss->server.flags |= StreamGetWscale(tdb->pkt, &tss->server.wscale); - CopyMacAddr(p, tmp, FROM_SERVER); - } + CopyMacAddr(tdb->pkt, tss, FROM_SERVER); } tcpStats.sessions_on_data++; - NewTcpSession(p, flow, dstPolicy, tmp); + NewTcpSession(tdb->pkt, flow, dstPolicy, tss); MODULE_PROFILE_END(s5TcpNewSessPerfStats); } -static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* config) +static int ProcessTcp(Flow* flow, TcpDataBlock* tdb, StreamTcpConfig* config) { int retcode = ACTION_NOTHING; int eventcode = 0; int got_ts = 0; int new_ssn = 0; int ts_action = ACTION_NOTHING; - TcpSession *tcpssn = NULL; - TcpTracker *talker = NULL; - TcpTracker *listener = NULL; + const tcp::TCPHdr* tcph = tdb->pkt->ptrs.tcph; + TcpSession* tcpssn = NULL; + TcpTracker* talker = NULL; + TcpTracker* listener = NULL; DEBUG_WRAP( const char* t = NULL; const char* l = NULL; ); PROFILE_VARS; @@ -1557,7 +1560,7 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* return retcode; } - tcpssn = (TcpSession*) flow->session; + tcpssn = ( TcpSession* ) flow->session; MODULE_PROFILE_START(s5TcpStatePerfStats); @@ -1565,7 +1568,7 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* { // FIXIT-L expected flow should be checked by flow_con before we // get here - char ignore = flow_con->expected_flow(flow, p); + char ignore = flow_con->expected_flow(flow, tdb->pkt); if (ignore) { @@ -1575,33 +1578,33 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* } bool require3Way = config->require_3whs(); - bool allow_midstream = config->midstream_allowed(p); + bool allow_midstream = config->midstream_allowed(tdb->pkt); - if (p->ptrs.tcph->is_syn_only()) + if (tcph->is_syn_only()) { DebugMessage(DEBUG_STREAM_STATE, "Stream SYN PACKET, establishing lightweight session direction.\n"); /* SYN packet from client */ flow->ssn_state.direction = FROM_CLIENT; flow->session_state |= STREAM_STATE_SYN; - if (require3Way || (StreamPacketHasWscale(p) & TF_WSCALE) || (p->dsize > 0)) + if (require3Way || (StreamPacketHasWscale(tdb->pkt) & TF_WSCALE) || (tdb->pkt->dsize > 0)) { /* Create TCP session if we * 1) require 3-WAY HS, OR * 2) client sent wscale option, OR * 3) have data */ - NewTcpSessionOnSyn(p, flow, tdb, config); + NewTcpSessionOnSyn( flow, tdb, config); new_ssn = 1; - NormalTrackECN(tcpssn, (TCPHdr*) p->ptrs.tcph, require3Way); + tcpssn->server.normalizer->ecn_tracker( (tcp::TCPHdr *) tcph, require3Way ); } /* Nothing left todo here */ } - else if (p->ptrs.tcph->is_syn_ack()) + else if (tcph->is_syn_ack()) { /* SYN-ACK from server */ - if ((flow->session_state == STREAM_STATE_NONE) || (flow->ssn_state.session_flags & SSNFLAG_RESET)) + if ((flow->session_state == STREAM_STATE_NONE) || (flow->get_session_flags() & SSNFLAG_RESET)) { DebugMessage(DEBUG_STREAM_STATE, "Stream SYN|ACK PACKET, establishing lightweight session direction.\n"); flow->ssn_state.direction = FROM_SERVER; @@ -1611,42 +1614,45 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* if (!require3Way || allow_midstream) { - NewTcpSessionOnSynAck(p, flow, tdb, config); + NewTcpSessionOnSynAck(flow, tdb, config); new_ssn = 1; } - NormalTrackECN(tcpssn, (TCPHdr*) p->ptrs.tcph, require3Way); + tcpssn->client.normalizer->ecn_tracker( (tcp::TCPHdr *) tcph, require3Way ); } - else if (p->ptrs.tcph->is_ack() && !p->ptrs.tcph->is_rst() && (flow->session_state & STREAM_STATE_SYN_ACK)) + else if (tcph->is_ack() && !tcph->is_rst() && (flow->session_state & STREAM_STATE_SYN_ACK)) { /* FIXIT: do we need to verify the ACK field is >= the seq of the SYN-ACK? 3-way Handshake complete, create TCP session */ flow->session_state |= STREAM_STATE_ACK | STREAM_STATE_ESTABLISHED; - NewTcpSessionOn3Way(p, flow, tdb, config); + NewTcpSessionOn3Way(flow, tdb, config); new_ssn = 1; - NormalTrackECN(tcpssn, (TCPHdr*) p->ptrs.tcph, require3Way); + tcpssn->server.normalizer->ecn_tracker( (tcp::TCPHdr *) tcph, require3Way ); StreamUpdatePerfBaseState(&sfBase, flow, TCP_STATE_ESTABLISHED); } - else if (p->dsize && (!require3Way || allow_midstream)) + else if (tdb->pkt->dsize && (!require3Way || allow_midstream)) { /* create session on data, need to figure out direction, etc Assume from client, can update later */ - if (p->ptrs.sp > p->ptrs.dp) + if (tdb->pkt->ptrs.sp > tdb->pkt->ptrs.dp) flow->ssn_state.direction = FROM_CLIENT; else flow->ssn_state.direction = FROM_SERVER; flow->session_state |= STREAM_STATE_MIDSTREAM; - flow->ssn_state.session_flags |= SSNFLAG_MIDSTREAM; + flow->set_session_flags( SSNFLAG_MIDSTREAM ); - NewTcpSessionOnData(p, flow, tdb, config); + NewTcpSessionOnData(flow, tdb, config); new_ssn = 1; - NormalTrackECN(tcpssn, (TCPHdr*) p->ptrs.tcph, require3Way); + if(flow->ssn_state.direction == FROM_CLIENT) + tcpssn->server.normalizer->ecn_tracker( (tcp::TCPHdr *) tcph, require3Way ); + else + tcpssn->client.normalizer->ecn_tracker( (tcp::TCPHdr *) tcph, require3Way ); if (flow->session_state & STREAM_STATE_ESTABLISHED) StreamUpdatePerfBaseState(&sfBase, flow, TCP_STATE_ESTABLISHED); } - else if (!p->dsize) + else if (!tdb->pkt->dsize) { /* Do nothing. */ MODULE_PROFILE_END(s5TcpStatePerfStats); @@ -1657,7 +1663,7 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* { /* If session is already marked as established */ if (!(flow->session_state & STREAM_STATE_ESTABLISHED) - && (!config->require_3whs() || config->midstream_allowed(p))) + && (!config->require_3whs() || config->midstream_allowed(tdb->pkt))) { /* If not requiring 3-way Handshake... */ @@ -1665,7 +1671,7 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* * or maybe on SYN-ACK, or anything else */ /* Need to update Lightweight session state */ - if (p->ptrs.tcph->is_syn_ack()) + if (tcph->is_syn_ack()) { /* SYN-ACK from server */ if (flow->session_state != STREAM_STATE_NONE) @@ -1673,20 +1679,20 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* flow->session_state |= STREAM_STATE_SYN_ACK; } } - else if (p->ptrs.tcph->is_ack() && (flow->session_state & STREAM_STATE_SYN_ACK)) + else if (tcph->is_ack() && (flow->session_state & STREAM_STATE_SYN_ACK)) { flow->session_state |= STREAM_STATE_ACK | STREAM_STATE_ESTABLISHED; StreamUpdatePerfBaseState(&sfBase, flow, TCP_STATE_ESTABLISHED); } } - if (p->ptrs.tcph->is_syn()) - NormalTrackECN(tcpssn, (TCPHdr*) p->ptrs.tcph, config->require_3whs()); + if (tcph->is_syn()) + tcpssn->server.normalizer->ecn_tracker( (tcp::TCPHdr *) tcph, config->require_3whs() ); } - if (p->packet_flags & PKT_FROM_SERVER) + if (tdb->pkt->packet_flags & PKT_FROM_SERVER) { DebugMessage(DEBUG_STREAM_STATE, "Stream: Updating on packet from server\n"); - flow->ssn_state.session_flags |= SSNFLAG_SEEN_SERVER; + flow->set_session_flags( SSNFLAG_SEEN_SERVER ); if (tcpssn->tcp_init) { @@ -1698,35 +1704,32 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* t = "Server"; l = "Client"); - if (talker && talker->s_mgr.state == TCP_STATE_LISTEN - && ((p->ptrs.tcph->th_flags & (TH_SYN | TH_ACK)) == TH_SYN)) - { + if( talker && ( talker->s_mgr.state == TCP_STATE_LISTEN ) && tcph->is_syn_only() ) eventcode |= EVENT_4WHS; - } + /* If we picked this guy up midstream, finish the initialization */ if ((flow->session_state & STREAM_STATE_MIDSTREAM) && !(flow->session_state & STREAM_STATE_ESTABLISHED)) { - FinishServerInit(p, tdb, tcpssn); - if ((p->ptrs.tcph->th_flags & TH_ECE) && ( flow->ssn_state.session_flags & SSNFLAG_ECN_CLIENT_QUERY )) - { - flow->ssn_state.session_flags |= SSNFLAG_ECN_SERVER_REPLY; - } + FinishServerInit(tdb, tcpssn); + if( tcph->are_flags_set( TH_ECE ) && ( flow->get_session_flags() & SSNFLAG_ECN_CLIENT_QUERY ) ) + flow->set_session_flags( SSNFLAG_ECN_SERVER_REPLY ); - if (flow->ssn_state.session_flags & SSNFLAG_SEEN_CLIENT) + if( flow->get_session_flags() & SSNFLAG_SEEN_CLIENT ) { // should TCP state go to established too? flow->session_state |= STREAM_STATE_ESTABLISHED; - flow->ssn_state.session_flags |= SSNFLAG_ESTABLISHED; - StreamUpdatePerfBaseState(&sfBase, flow, TCP_STATE_ESTABLISHED); + flow->set_session_flags( SSNFLAG_ESTABLISHED ); + StreamUpdatePerfBaseState( &sfBase, flow, TCP_STATE_ESTABLISHED ); } } if (!flow->inner_server_ttl) - flow->set_ttl(p, false); - } else + flow->set_ttl(tdb->pkt, false); + } + else { DebugMessage(DEBUG_STREAM_STATE, "Stream: Updating on packet from client\n"); /* if we got here we had to see the SYN already... */ - flow->ssn_state.session_flags |= SSNFLAG_SEEN_CLIENT; + flow->set_session_flags( SSNFLAG_SEEN_CLIENT ); if (tcpssn->tcp_init) { talker = &tcpssn->client; @@ -1740,30 +1743,30 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* if ((flow->session_state & STREAM_STATE_MIDSTREAM) && !(flow->session_state & STREAM_STATE_ESTABLISHED)) { /* Midstream and seen server. */ - if (flow->ssn_state.session_flags & SSNFLAG_SEEN_SERVER) + if (flow->get_session_flags() & SSNFLAG_SEEN_SERVER) { flow->session_state |= STREAM_STATE_ESTABLISHED; - flow->ssn_state.session_flags |= SSNFLAG_ESTABLISHED; + flow->set_session_flags( SSNFLAG_ESTABLISHED ); } } if (!flow->inner_client_ttl) - flow->set_ttl(p, true); + flow->set_ttl(tdb->pkt, true); } /* * check for SYN on reset session */ - if ((flow->ssn_state.session_flags & SSNFLAG_RESET) && (p->ptrs.tcph->th_flags & TH_SYN)) + if( ( flow->get_session_flags() & SSNFLAG_RESET ) && tcph->is_syn() ) { - if (!tcpssn->tcp_init || (listener->s_mgr.state == TCP_STATE_CLOSED) - || (talker->s_mgr.state == TCP_STATE_CLOSED)) + if ( !tcpssn->tcp_init || ( listener->s_mgr.state == TCP_STATE_CLOSED ) + || ( talker->s_mgr.state == TCP_STATE_CLOSED ) ) { /* Listener previously issued a reset Talker is re-SYN-ing */ // FIXIT-L this leads to bogus 129:20 TcpSessionCleanup(flow, 1); - if (p->ptrs.tcph->th_flags & TH_RST) + if( tcph->is_rst() ) { /* FIXIT-M In inline mode, only one of the normalizations * can occur. If the first normalization @@ -1777,50 +1780,44 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* */ /* Got SYN/RST. We're done. */ - NormalTrimPayloadIfSyn(p, 0, tdb); - NormalTrimPayloadIfRst(p, 0, tdb); + listener->normalizer->trim_syn_payload( tdb ); + listener->normalizer->trim_rst_payload( tdb ); MODULE_PROFILE_END(s5TcpStatePerfStats); return retcode | ACTION_RST; } - else if (p->ptrs.tcph->is_syn_only()) + else if (tcph->is_syn_only()) { flow->ssn_state.direction = FROM_CLIENT; flow->session_state = STREAM_STATE_SYN; - flow->set_ttl(p, true); - NewTcpSessionOnSyn(p, flow, tdb, config); + flow->set_ttl(tdb->pkt, true); + NewTcpSessionOnSyn( flow, tdb, config); tcpStats.resyns++; new_ssn = 1; bool require3Way = config->require_3whs(); - NormalTrackECN(tcpssn, (TCPHdr*) p->ptrs.tcph, require3Way); - - { - listener = &tcpssn->server; - talker = &tcpssn->client; - } - flow->ssn_state.session_flags = SSNFLAG_SEEN_CLIENT; + listener = &tcpssn->server; + talker = &tcpssn->client; + listener->normalizer->ecn_tracker( (tcp::TCPHdr *) tcph, require3Way ); + flow->update_session_flags( SSNFLAG_SEEN_CLIENT ); } - else if (p->ptrs.tcph->is_syn_ack()) + else if (tcph->is_syn_ack()) { - if (config->midstream_allowed(p)) + if (config->midstream_allowed(tdb->pkt)) { flow->ssn_state.direction = FROM_SERVER; flow->session_state = STREAM_STATE_SYN_ACK; - flow->set_ttl(p, false); - NewTcpSessionOnSynAck(p, flow, tdb, config); + flow->set_ttl(tdb->pkt, false); + NewTcpSessionOnSynAck( flow, tdb, config); tcpStats.resyns++; tcpssn = (TcpSession*) flow->session; new_ssn = 1; } bool require3Way = config->require_3whs(); - NormalTrackECN(tcpssn, (TCPHdr*) p->ptrs.tcph, require3Way); - - { - listener = &tcpssn->client; - talker = &tcpssn->server; - } - flow->ssn_state.session_flags = SSNFLAG_SEEN_SERVER; + listener = &tcpssn->client; + talker = &tcpssn->server; + listener->normalizer->ecn_tracker( (tcp::TCPHdr *) tcph, require3Way ); + flow->update_session_flags( SSNFLAG_SEEN_SERVER ); } } @@ -1829,30 +1826,30 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* // FIXIT-L why flush here instead of just purge? // s5_ignored_session() may be disabling detection too soon if we really want to flush - if (stream.ignored_session(flow, p)) + if (stream.ignored_session(flow, tdb->pkt)) { if (talker && (talker->flags & TF_FORCE_FLUSH)) { - tcpssn->flush_talker(p); + tcpssn->flush_talker(tdb->pkt); talker->flags &= ~TF_FORCE_FLUSH; } if (listener && (listener->flags & TF_FORCE_FLUSH)) { - tcpssn->flush_listener(p); + tcpssn->flush_listener(tdb->pkt); listener->flags &= ~TF_FORCE_FLUSH; } - p->packet_flags |= PKT_IGNORE; + tdb->pkt->packet_flags |= PKT_IGNORE; retcode |= ACTION_DISABLE_INSPECTION; } /* Handle data on SYN */ - if ((p->dsize) && p->ptrs.tcph->is_syn()) + if ((tdb->pkt->dsize) && tcph->is_syn()) { /* MacOS accepts data on SYN, so don't alert if policy is MACOS */ if (talker->os_policy != STREAM_POLICY_MACOS) { // remove data on SYN - NormalTrimPayloadIfSyn(p, 0, tdb); + listener->normalizer->trim_syn_payload( tdb ); if (Normalize_GetMode(NORM_TCP_TRIM_SYN) == NORM_MODE_OFF) { @@ -1875,9 +1872,9 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* DebugFormat(DEBUG_STREAM_STATE, " %s state: %s(%d)\n", l, state_names[listener->s_mgr.state], listener->s_mgr.state); // may find better placement to eliminate redundant flag checks - if (p->ptrs.tcph->th_flags & TH_SYN) + if( tcph->is_syn() ) talker->s_mgr.sub_state |= SUB_SYN_SENT; - if (p->ptrs.tcph->th_flags & TH_ACK) + if( tcph->is_ack() ) talker->s_mgr.sub_state |= SUB_ACK_SENT; /* @@ -1885,7 +1882,7 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* */ if ((TCP_STATE_SYN_SENT == listener->s_mgr.state) && (TCP_STATE_LISTEN == talker->s_mgr.state)) { - if (p->ptrs.tcph->th_flags & TH_ACK) + if( tcph->is_ack() ) { /* * make sure we've got a valid segment @@ -1894,25 +1891,25 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* { DebugMessage(DEBUG_STREAM_STATE, "Pkt ack is out of bounds, bailing!\n"); inc_tcp_discards(); - NormalTrimPayloadIfWin(p, 0, tdb); + listener->normalizer->trim_win_payload( tdb ); LogTcpEvents(eventcode); MODULE_PROFILE_END(s5TcpStatePerfStats); return retcode | ACTION_BAD_PKT; } } - talker->flags |= StreamGetTcpTimestamp(p, &tdb->ts, 0); + talker->flags |= talker->normalizer->get_tcp_timestamp(tdb, false); if (tdb->ts == 0) talker->flags |= TF_TSTAMP_ZERO; /* * catch resets sent by server */ - if (p->ptrs.tcph->th_flags & TH_RST) + if( tcph->is_rst() ) { DebugMessage(DEBUG_STREAM_STATE, "got RST\n"); - NormalTrimPayloadIfRst(p, 0, tdb); + listener->normalizer->trim_rst_payload( tdb ); /* Reset is valid when in SYN_SENT if the * ack field ACKs the SYN. @@ -1925,7 +1922,7 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* * additional data sent from one side or the other isn't * processed (and is dropped in inline mode). */ - flow->ssn_state.session_flags |= SSNFLAG_RESET; + flow->set_session_flags( SSNFLAG_RESET ); talker->s_mgr.state = TCP_STATE_CLOSED; StreamUpdatePerfBaseState(&sfBase, flow, TCP_STATE_CLOSING); /* Leave listener open, data may be in transit */ @@ -1937,21 +1934,19 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* DebugMessage(DEBUG_STREAM_STATE, "bad sequence number, bailing\n"); inc_tcp_discards(); eventcode |= EVENT_BAD_RST; - NormalDropPacketIf(p, NORM_TCP_BLOCK); + listener->normalizer->packet_dropper(tdb, NORM_TCP_BLOCK); LogTcpEvents(eventcode); MODULE_PROFILE_END(s5TcpStatePerfStats); return retcode; } - /* - * finish up server init - */ - if (p->ptrs.tcph->th_flags & TH_SYN) + // finish up server init + if( tcph->is_syn() ) { - FinishServerInit(p, tdb, tcpssn); + FinishServerInit(tdb, tcpssn); if (talker->flags & TF_TSTAMP) { - talker->ts_last_pkt = p->pkth->ts.tv_sec; + talker->ts_last_pkt = tdb->pkt->pkth->ts.tv_sec; talker->ts_last = tdb->ts; } @@ -1962,8 +1957,8 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* DebugMessage(DEBUG_STREAM_STATE, "Finish server init didn't get called!\n"); } - if ((p->ptrs.tcph->th_flags & TH_ECE) && ( flow->ssn_state.session_flags & SSNFLAG_ECN_CLIENT_QUERY) ) - flow->ssn_state.session_flags |= SSNFLAG_ECN_SERVER_REPLY; + if( tcph->are_flags_set( TH_ECE ) && ( flow->get_session_flags() & SSNFLAG_ECN_CLIENT_QUERY ) ) + flow->set_session_flags( SSNFLAG_ECN_SERVER_REPLY ); /* * explicitly set the state @@ -1981,31 +1976,25 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* * This is per RFC 1323. */ if ((talker->flags & TF_WSCALE) && (listener->flags & TF_WSCALE)) - { tdb->win <<= talker->wscale; - } /* Check for session hijacking -- compare mac address to the ones - * that were recorded at session startup. - */ + * that were recorded at session startup. */ #ifdef DAQ_PKT_FLAG_PRE_ROUTING - if (!(p->pkth->flags & DAQ_PKT_FLAG_PRE_ROUTING)) + if (!(tdb->pkt->pkth->flags & DAQ_PKT_FLAG_PRE_ROUTING)) #endif { - eventcode |= ValidMacAddress(talker, listener, p); + eventcode |= ValidMacAddress(talker, listener, tdb->pkt); } - /* Check timestamps */ - ts_action = ValidTimestamp(talker, listener, tdb, p, &eventcode, &got_ts); + ts_action = listener->normalizer->handle_paws( tdb, &eventcode, &got_ts ); - /* - * check RST validity - */ - if (p->ptrs.tcph->th_flags & TH_RST) + // check RST validity + if( tcph->is_rst() ) { - NormalTrimPayloadIfRst(p, 0, tdb); + listener->normalizer->trim_rst_payload( tdb ); - if (ValidRst(flow, listener, tdb)) + if (listener->normalizer->validate_rst( tdb )) { DebugMessage(DEBUG_STREAM_STATE, "Got RST, bailing\n"); @@ -2014,13 +2003,13 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* || listener->s_mgr.state == TCP_STATE_CLOSE_WAIT || listener->s_mgr.state == TCP_STATE_CLOSING) { - tcpssn->flush_talker(p); - tcpssn->flush_listener(p); + tcpssn->flush_talker(tdb->pkt); + tcpssn->flush_listener(tdb->pkt); tcpssn->set_splitter(true, nullptr); tcpssn->set_splitter(false, nullptr); flow->free_application_data(); } - flow->ssn_state.session_flags |= SSNFLAG_RESET; + flow->set_session_flags( SSNFLAG_RESET ); talker->s_mgr.state = TCP_STATE_CLOSED; talker->s_mgr.sub_state |= SUB_RST_SENT; StreamUpdatePerfBaseState(&sfBase, flow, TCP_STATE_CLOSING); @@ -2039,7 +2028,7 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* DebugMessage(DEBUG_STREAM_STATE, "bad sequence number, bailing\n"); inc_tcp_discards(); eventcode |= EVENT_BAD_RST; - NormalDropPacketIf(p, NORM_TCP_BLOCK); + listener->normalizer->packet_dropper(tdb, NORM_TCP_BLOCK); LogTcpEvents(eventcode); MODULE_PROFILE_END(s5TcpStatePerfStats); return retcode | ts_action; @@ -2049,11 +2038,11 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* /* check for valid seqeuence/retrans */ if (listener->config->policy != STREAM_POLICY_PROXY and (listener->s_mgr.state >= TCP_STATE_ESTABLISHED) - and !ValidSeq(p, flow, listener, tdb)) + and !ValidSeq(flow, listener, tdb)) { DebugMessage(DEBUG_STREAM_STATE, "bad sequence number, bailing\n"); inc_tcp_discards(); - NormalTrimPayloadIfWin(p, 0, tdb); + listener->normalizer->trim_win_payload( tdb ); LogTcpEvents(eventcode); MODULE_PROFILE_END(s5TcpStatePerfStats); return retcode | ts_action; @@ -2078,11 +2067,11 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* if (got_ts && SEQ_EQ(listener->r_win_base, tdb->seq)) { if ((int32_t) (tdb->ts - talker->ts_last) >= 0|| - (uint32_t)p->pkth->ts.tv_sec >= talker->ts_last_pkt+PAWS_24DAYS) + (uint32_t)tdb->pkt->pkth->ts.tv_sec >= talker->ts_last_pkt + PAWS_24DAYS) { DebugMessage(DEBUG_STREAM_STATE, "updating timestamps...\n"); talker->ts_last = tdb->ts; - talker->ts_last_pkt = p->pkth->ts.tv_sec; + talker->ts_last_pkt = tdb->pkt->pkth->ts.tv_sec; } } else { @@ -2092,13 +2081,13 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* /* * check for repeat SYNs */ - if (!new_ssn && ((p->ptrs.tcph->th_flags & (TH_SYN | TH_ACK)) == TH_SYN)) + if( !new_ssn && tcph->is_syn_only() ) { int action; - if (!SEQ_EQ(tdb->seq, talker->isn) && NormalDropPacketIf(p, NORM_TCP_BLOCK)) + if (!SEQ_EQ(tdb->seq, talker->isn) && listener->normalizer->packet_dropper(tdb, NORM_TCP_BLOCK)) action = ACTION_BAD_PKT; else if (talker->s_mgr.state >= TCP_STATE_ESTABLISHED) - action = RepeatedSyn(listener, talker, tdb, tcpssn); + action = listener->normalizer->handle_repeated_syn( tdb ); else action = ACTION_NOTHING; @@ -2123,22 +2112,22 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* /* got a window too large, alert! */ eventcode |= EVENT_WINDOW_TOO_LARGE; inc_tcp_discards(); - NormalDropPacketIf(p, NORM_TCP_BLOCK); + listener->normalizer->packet_dropper(tdb, NORM_TCP_BLOCK); LogTcpEvents(eventcode); MODULE_PROFILE_END(s5TcpStatePerfStats); return retcode | ACTION_BAD_PKT; } - else if ((p->packet_flags & PKT_FROM_CLIENT) && (tdb->win <= SLAM_MAX) + else if ((tdb->pkt->packet_flags & PKT_FROM_CLIENT) && (tdb->win <= SLAM_MAX) && (tdb->ack == listener->isn + 1) - && !(p->ptrs.tcph->th_flags & (TH_FIN | TH_RST)) - && !(flow->ssn_state.session_flags & SSNFLAG_MIDSTREAM)) + && !( tcph->is_fin() | tcph->is_rst() ) + && !(flow->get_session_flags() & SSNFLAG_MIDSTREAM)) { DebugMessage(DEBUG_STREAM_STATE, "Window slammed shut!\n"); /* got a window slam alert! */ eventcode |= EVENT_WINDOW_SLAM; inc_tcp_discards(); - if (NormalDropPacketIf(p, NORM_TCP_BLOCK)) + if (listener->normalizer->packet_dropper(tdb, NORM_TCP_BLOCK)) { LogTcpEvents(eventcode); MODULE_PROFILE_END(s5TcpStatePerfStats); @@ -2160,10 +2149,8 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* } } - /* - * process ACK flags - */ - if (p->ptrs.tcph->th_flags & TH_ACK) + // process ACK flags + if( tcph->is_ack() ) { DebugMessage(DEBUG_STREAM_STATE, "Got an ACK...\n"); DebugFormat(DEBUG_STREAM_STATE, " %s [listener] state: %s\n", l, state_names[listener->s_mgr.state]); @@ -2177,21 +2164,20 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* DebugMessage(DEBUG_STREAM_STATE, "listener state is SYN_SENT...\n"); if (IsBetween(listener->l_unackd, listener->l_nxt_seq, tdb->ack)) { - UpdateSsn(p, listener, talker, tdb); - flow->ssn_state.session_flags |= SSNFLAG_ESTABLISHED; + UpdateSsn(listener, talker, tdb); + flow->set_session_flags( SSNFLAG_ESTABLISHED ); flow->session_state |= STREAM_STATE_ESTABLISHED; listener->s_mgr.state = TCP_STATE_ESTABLISHED; talker->s_mgr.state = TCP_STATE_ESTABLISHED; - StreamUpdatePerfBaseState(&sfBase, flow, - TCP_STATE_ESTABLISHED); + StreamUpdatePerfBaseState(&sfBase, flow, TCP_STATE_ESTABLISHED); /* Indicate this packet completes 3-way handshake */ - p->packet_flags |= PKT_STREAM_TWH; + tdb->pkt->packet_flags |= PKT_STREAM_TWH; } talker->flags |= got_ts; if (got_ts && SEQ_EQ(listener->r_nxt_ack, tdb->seq)) { - talker->ts_last_pkt = p->pkth->ts.tv_sec; + talker->ts_last_pkt = tdb->pkt->pkth->ts.tv_sec; talker->ts_last = tdb->ts; } @@ -2199,11 +2185,11 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* case TCP_STATE_ESTABLISHED: case TCP_STATE_CLOSE_WAIT: - UpdateSsn(p, listener, talker, tdb); + UpdateSsn( listener, talker, tdb); break; case TCP_STATE_FIN_WAIT_1: - UpdateSsn(p, listener, talker, tdb); + UpdateSsn(listener, talker, tdb); DebugFormat(DEBUG_STREAM_STATE, "tdb->ack %X >= talker->r_nxt_ack %X\n", tdb->ack, talker->r_nxt_ack); @@ -2214,7 +2200,7 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* eventcode |= EVENT_WINDOW_SLAM; inc_tcp_discards(); - if (NormalDropPacketIf(p, NORM_TCP_BLOCK)) + if (listener->normalizer->packet_dropper(tdb, NORM_TCP_BLOCK)) { LogTcpEvents(eventcode); MODULE_PROFILE_END(s5TcpStatePerfStats); @@ -2224,16 +2210,16 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* listener->s_mgr.state = TCP_STATE_FIN_WAIT_2; - if ((p->ptrs.tcph->th_flags & TH_FIN)) + if( tcph->is_fin() ) { DebugMessage(DEBUG_STREAM_STATE, "seq ok, setting state!\n"); if (talker->s_mgr.state_queue == TCP_STATE_NONE) { talker->s_mgr.state = TCP_STATE_LAST_ACK; - EndOfFileHandle(p, tcpssn); + EndOfFileHandle(tdb->pkt, tcpssn); } - if (flow->ssn_state.session_flags & SSNFLAG_MIDSTREAM) + if (flow->get_session_flags() & SSNFLAG_MIDSTREAM) { // FIXIT-L this should be handled below in fin section // but midstream sessions fail the seq test @@ -2254,25 +2240,25 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* break; case TCP_STATE_FIN_WAIT_2: - UpdateSsn(p, listener, talker, tdb); + UpdateSsn(listener, talker, tdb); if (SEQ_GT(tdb->ack, listener->l_nxt_seq)) { eventcode |= EVENT_BAD_ACK; LogTcpEvents(eventcode); - NormalDropPacketIf(p, NORM_TCP_BLOCK); + listener->normalizer->packet_dropper(tdb, NORM_TCP_BLOCK); MODULE_PROFILE_END(s5TcpStatePerfStats); return retcode | ACTION_BAD_PKT; } break; case TCP_STATE_CLOSING: - UpdateSsn(p, listener, talker, tdb); + UpdateSsn(listener, talker, tdb); if (SEQ_GEQ(tdb->end_seq, listener->r_nxt_ack)) listener->s_mgr.state = TCP_STATE_TIME_WAIT; break; case TCP_STATE_LAST_ACK: - UpdateSsn(p, listener, talker, tdb); + UpdateSsn( listener, talker, tdb); if (SEQ_EQ(tdb->ack, listener->l_nxt_seq)) listener->s_mgr.state = TCP_STATE_CLOSED; @@ -2283,13 +2269,13 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* break; } - CheckFlushPolicyOnAck(tcpssn, talker, listener, p); + CheckFlushPolicyOnAck(tcpssn, talker, listener,tdb->pkt); } /* * handle data in the segment */ - if (p->dsize) + if (tdb->pkt->dsize) { DebugFormat(DEBUG_STREAM_STATE, " %s state: %s(%d) getting data\n", l, state_names[listener->s_mgr.state], listener->s_mgr.state); @@ -2304,13 +2290,13 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* //EventDataOnClosed(talker->config); eventcode |= EVENT_DATA_ON_CLOSED; retcode |= ACTION_BAD_PKT; - NormalDropPacketIf(p, NORM_TCP_BLOCK); + listener->normalizer->packet_dropper(tdb, NORM_TCP_BLOCK); } else if (TCP_STATE_CLOSED == talker->s_mgr.state) { /* data on a segment when we're not accepting data any more alert! */ - if (flow->ssn_state.session_flags & SSNFLAG_RESET) + if (flow->get_session_flags() & SSNFLAG_RESET) { //EventDataAfterReset(listener->config); if (talker->s_mgr.sub_state & SUB_RST_SENT) @@ -2324,7 +2310,7 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* eventcode |= EVENT_DATA_ON_CLOSED; } retcode |= ACTION_BAD_PKT; - NormalDropPacketIf(p, NORM_TCP_BLOCK); + listener->normalizer->packet_dropper(tdb, NORM_TCP_BLOCK); } else { @@ -2335,7 +2321,7 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* { // these normalizations can't be done if we missed setup. and // window is zero in one direction until we've seen both sides. - if (!(flow->ssn_state.session_flags & SSNFLAG_MIDSTREAM)) + if (!(flow->get_session_flags() & SSNFLAG_MIDSTREAM)) { // sender of syn w/mss limits payloads from peer // since we store mss on sender side, use listener mss @@ -2343,36 +2329,33 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* TcpTracker* st = listener; // trim to fit in window and mss as needed - NormalTrimPayloadIfWin(p, (st->r_win_base + st->l_window) - st->r_nxt_ack, tdb); + st->normalizer->trim_win_payload(tdb, (st->r_win_base + st->l_window) - st->r_nxt_ack); if (st->mss) - NormalTrimPayloadIfMss(p, st->mss, tdb); + st->normalizer->trim_mss_payload( tdb, st->mss ); - NormalCheckECN(tcpssn, p); + st->normalizer->ecn_stripper( tdb->pkt ); } } - /* - * dunno if this is RFC but fragroute testing expects it - * for the record, I've seen FTP data sessions that send - * data packets with no tcp flags set - */ - if ((p->ptrs.tcph->th_flags != 0) + // dunno if this is RFC but fragroute testing expects it for the record, + // I've seen FTP data sessions that send data packets with no tcp flags set + if ((tcph->th_flags != 0) or (config->policy == STREAM_POLICY_LINUX) or (config->policy == STREAM_POLICY_PROXY)) { - ProcessTcpData(p, listener, tcpssn, tdb, config); + ProcessTcpData( listener, tcpssn, tdb, config); } else { eventcode |= EVENT_DATA_WITHOUT_FLAGS; - NormalDropPacketIf(p, NORM_TCP_BLOCK); + listener->normalizer->packet_dropper(tdb, NORM_TCP_BLOCK); } } - CheckFlushPolicyOnData(tcpssn, talker, listener, p); + CheckFlushPolicyOnData(tcpssn, talker, listener, tdb->pkt); } - if (p->ptrs.tcph->th_flags & TH_FIN) + if( tcph->is_fin() ) { DebugMessage(DEBUG_STREAM_STATE, "Got a FIN...\n"); DebugFormat(DEBUG_STREAM_STATE, " %s state: %s(%d)\n", l, state_names[talker->s_mgr.state], talker->s_mgr.state); @@ -2404,7 +2387,7 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* && (listener->flush_policy != STREAM_FLPOLICY_ON_DATA) && Normalize_IsEnabled(NORM_TCP_IPS)) { - p->packet_flags |= PKT_PDU_TAIL; + tdb->pkt->packet_flags |= PKT_PDU_TAIL; } } switch (talker->s_mgr.state) @@ -2415,10 +2398,10 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* talker->s_mgr.state_queue = TCP_STATE_CLOSING; talker->s_mgr.state = TCP_STATE_FIN_WAIT_1; - EndOfFileHandle(p, tcpssn); + EndOfFileHandle(tdb->pkt, tcpssn); - if (!p->dsize) - CheckFlushPolicyOnData(tcpssn, talker, listener, p); + if (!tdb->pkt->dsize) + CheckFlushPolicyOnData(tcpssn, talker, listener, tdb->pkt); StreamUpdatePerfBaseState(&sfBase, tcpssn->flow, TCP_STATE_CLOSING); break; @@ -2428,8 +2411,8 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* break; case TCP_STATE_FIN_WAIT_1: - if (!p->dsize) - RetransmitHandle(p, tcpssn); + if (!tdb->pkt->dsize) + RetransmitHandle(tdb->pkt, tcpssn); break; default: @@ -2439,7 +2422,7 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* if ((talker->s_mgr.state == TCP_STATE_FIN_WAIT_1) || (talker->s_mgr.state == TCP_STATE_LAST_ACK)) { - uint32_t end_seq = (flow->ssn_state.session_flags & SSNFLAG_MIDSTREAM) + uint32_t end_seq = (flow->get_session_flags() & SSNFLAG_MIDSTREAM) ? tdb->end_seq - 1 : tdb->end_seq; if ((listener->s_mgr.expected_flags == TH_ACK) && SEQ_GEQ(end_seq, listener->s_mgr.transition_seq)) @@ -2447,7 +2430,7 @@ static int ProcessTcp(Flow* flow, Packet* p, TcpDataBlock* tdb, StreamTcpConfig* DebugMessage(DEBUG_STREAM_STATE, "FIN beyond previous, ignoring\n"); eventcode |= EVENT_BAD_FIN; LogTcpEvents(eventcode); - NormalDropPacketIf(p, NORM_TCP_BLOCK); + listener->normalizer->packet_dropper(tdb, NORM_TCP_BLOCK); MODULE_PROFILE_END(s5TcpStatePerfStats); return retcode | ACTION_BAD_PKT; } @@ -2481,28 +2464,23 @@ dupfin: DebugFormat(DEBUG_STREAM_STATE, " %s [talker] state: %s\n", t, state_names[talker->s_mgr.state]); DebugFormat(DEBUG_STREAM_STATE, " %s state: %s(%d)\n", l, state_names[listener->s_mgr.state], listener->s_mgr.state); - /* - * handle TIME_WAIT timer stuff - */ + // handle TIME_WAIT timer stuff if ((talker->s_mgr.state == TCP_STATE_TIME_WAIT && listener->s_mgr.state == TCP_STATE_CLOSED) || (listener->s_mgr.state == TCP_STATE_TIME_WAIT && talker->s_mgr.state == TCP_STATE_CLOSED) || (listener->s_mgr.state == TCP_STATE_TIME_WAIT && talker->s_mgr.state == TCP_STATE_TIME_WAIT)) { - // The last ACK is a part of the session. - // Delete the session after processing is complete. + // The last ACK is a part of the session. Delete the session after processing is complete. LogTcpEvents(eventcode); - TcpSessionCleanup(flow, 0, p); + TcpSessionCleanup(flow, 0, tdb->pkt); flow->session_state |= STREAM_STATE_CLOSED; MODULE_PROFILE_END(s5TcpStatePerfStats); return retcode | ACTION_LWSSN_CLOSED; } - else if (listener->s_mgr.state == TCP_STATE_CLOSED && talker->s_mgr.state == TCP_STATE_SYN_SENT) + else if( listener->s_mgr.state == TCP_STATE_CLOSED + && talker->s_mgr.state == TCP_STATE_SYN_SENT ) { - if ( ( p->ptrs.tcph->th_flags & TH_SYN ) && !(p->ptrs.tcph->th_flags & TH_ACK) - && !(p->ptrs.tcph->th_flags & TH_RST)) - { - flow->set_expire(p, config->session_timeout); - } + if( tcph->is_syn_only() ) + flow->set_expire(tdb->pkt, config->session_timeout); } LogTcpEvents(eventcode); @@ -2574,7 +2552,7 @@ void TcpSession::cleanup() void TcpSession::clear() { - if (tcp_init) + if( tcp_init ) // this does NOT flush data TcpSessionClear(flow, this, 1); } @@ -3067,7 +3045,7 @@ midstream_pickup_allowed: if (!p->ptrs.tcph->is_syn_ack() if (stream.expired_session(flow, p)) { /* Session is timed out */ - if (flow->ssn_state.session_flags & SSNFLAG_RESET) + if (flow->get_session_flags() & SSNFLAG_RESET) { /* If this one has been reset, delete the TCP * portion, and start a new. */ @@ -3083,7 +3061,7 @@ midstream_pickup_allowed: if (!p->ptrs.tcph->is_syn_ack() tcpStats.timeouts++; } - status = ProcessTcp(flow, p, &tdb, config); + status = ProcessTcp(flow, &tdb, config); DebugMessage(DEBUG_STREAM_STATE, "Finished Stream TCP cleanly!\n---------------------------------------------------\n"); diff --git a/src/stream/tcp/tcp_session.h b/src/stream/tcp/tcp_session.h index fa56049f5..84de55bf5 100644 --- a/src/stream/tcp/tcp_session.h +++ b/src/stream/tcp/tcp_session.h @@ -28,6 +28,8 @@ #include "stream/paf.h" #include "flow/session.h" +class TcpNormalizer; + // TBD-EDM - these includes are for functions moved to a new file to group related functionality // in specific files ... these functional groups will be further refactored as the stream tcp // rewrite continues... @@ -134,6 +136,7 @@ struct TcpTracker uint32_t xtradata_mask; /* extra data available to log */ uint16_t os_policy; + TcpNormalizer* normalizer; uint16_t reassembly_policy; uint16_t wscale; /* window scale setting */