#include "main/snort_config.h"
#include "main/thread_config.h"
#include "packet_io/active.h"
+#include "packet_tracer/packet_tracer.h"
#include "time/packet_time.h"
#include "file_flows.h"
if (!node)
return 0;
- time_t now = packet_time();
+ struct timeval now;
+ packet_gettimeofday(&now);
+
// only recycle expired nodes
- if (now > node->expires)
+ if (timercmp(&node->cache_expire_time, &now, <))
{
delete node->file;
return 0;
return 0;
}
+// Return the time in ms since we started waiting for pending file lookup.
+static int64_t time_elapsed_ms(struct timeval* now, struct timeval* expire_time, int64_t lookup_timeout)
+{
+ if(!now or !now->tv_sec or !expire_time or !expire_time->tv_sec)
+ return 0;
+
+ return lookup_timeout * 1000 + timersub_ms(now, expire_time);
+}
+
FileCache::FileCache(int64_t max_files_cached)
{
max_files = max_files_cached;
* after that anyway because the file that
* caused this will be gone.
*/
- time_t now = snort::packet_time();
- new_node.expires = now + timeout;
+ struct timeval now;
+ packet_gettimeofday(&now);
+
+ struct timeval time_to_add = { timeout, 0 };
+ timeradd(&now, &time_to_add, &new_node.cache_expire_time);
+
new_node.file = new FileContext;
std::lock_guard<std::mutex> lock(cache_mutex);
return nullptr;
}
- time_t now = packet_time();
- if (node->expires && now > node->expires)
+ struct timeval now;
+ packet_gettimeofday(&now);
+
+ if (timercmp(&node->cache_expire_time, &now, <))
{
xhash_free_node(fileHash, hash_node);
return nullptr;
}
- if (node->expires < now + timeout)
- node->expires = now + timeout;
+ struct timeval next_expire_time;
+ struct timeval time_to_add = { timeout, 0 };
+ timeradd(&now, &time_to_add, &next_expire_time);
+
+ // Refresh the timer on the cache.
+ if (timercmp(&node->cache_expire_time, &next_expire_time, <))
+ node->cache_expire_time = next_expire_time;
+
return node->file;
}
return 0;
}
-bool FileCache::apply_verdict(Packet* p, FileInfo* file, FileVerdict verdict,
+bool FileCache::apply_verdict(Packet* p, FileContext* file_ctx, FileVerdict verdict,
bool resume, FilePolicyBase* policy)
{
Flow* flow = p->flow;
Active* act = p->active;
+ struct timeval now = {0, 0};
+ struct timeval add_time;
- file->verdict = verdict;
+ if (verdict != FILE_VERDICT_PENDING)
+ timerclear(&file_ctx->pending_expire_time);
+
+ file_ctx->verdict = verdict;
switch (verdict)
{
return false;
case FILE_VERDICT_LOG:
if (resume)
- policy->log_file_action(flow, file, FILE_RESUME_LOG);
+ policy->log_file_action(flow, file_ctx, FILE_RESUME_LOG);
return false;
case FILE_VERDICT_BLOCK:
// can't block session inside a session
act->set_delayed_action(Active::ACT_RESET, true);
break;
case FILE_VERDICT_PENDING:
- act->set_delayed_action(Active::ACT_DROP, true);
- if (resume)
- policy->log_file_action(flow, file, FILE_RESUME_BLOCK);
+ packet_gettimeofday(&now);
+
+ if (timerisset(&file_ctx->pending_expire_time) and
+ timercmp(&file_ctx->pending_expire_time, &now, <))
+ {
+ // Timed out while waiting for pending verdict.
+ FileConfig* fc = get_file_config(SnortConfig::get_conf());
+
+ // Block session on timeout if configured, otherwise use the
+ // current action.
+ if (fc->block_timeout_lookup)
+ act->set_delayed_action(Active::ACT_RESET, true);
+
+ if (resume)
+ policy->log_file_action(flow, file_ctx, FILE_RESUME_BLOCK);
+
+ if (PacketTracer::is_active())
+ {
+ PacketTracer::log("File signature lookup: timed out after %" PRIi64 "ms.\n", time_elapsed_ms(&now, &file_ctx->pending_expire_time, lookup_timeout));
+ }
+ }
else
{
- store_verdict(flow, file, lookup_timeout);
- FileFlows* files = FileFlows::get_file_flows(flow);
- if (files)
- files->add_pending_file(file->get_file_id());
+ // Add packet to retry queue while we wait for response.
+
+ if (!timerisset(&file_ctx->pending_expire_time))
+ {
+ add_time = { lookup_timeout, 0 };
+ timeradd(&now, &add_time, &file_ctx->pending_expire_time);
+
+ if (PacketTracer::is_active())
+ PacketTracer::log("File signature lookup: adding new packet to retry queue.\n");
+ }
+ else if (PacketTracer::is_active())
+ {
+ // Won't add packet to retry queue if it is a retransmit
+ // and not from the retry queue since it should already
+ // be there.
+ if (!(p->packet_flags & PKT_RETRANSMIT) or
+ p->pkth->flags & DAQ_PKT_FLAG_RETRY_PACKET)
+ {
+ PacketTracer::log("File signature lookup: adding packet to retry queue. Resume=%d, Waited %" PRIi64 "ms.\n", resume, time_elapsed_ms(&now, &file_ctx->pending_expire_time, lookup_timeout));
+ }
+ }
+
+ act->set_delayed_action(Active::ACT_RETRY, true);
+
+ if (resume)
+ policy->log_file_action(flow, file_ctx, FILE_RESUME_BLOCK);
+ else if (store_verdict(flow, file_ctx, lookup_timeout) != 0)
+ act->set_delayed_action(Active::ACT_DROP, true);
+ else
+ {
+ FileFlows* files = FileFlows::get_file_flows(flow);
+ if (files)
+ files->add_pending_file(file_ctx->get_file_id());
+ }
}
return true;
default:
}
if (resume)
- policy->log_file_action(flow, file, FILE_RESUME_BLOCK);
+ policy->log_file_action(flow, file_ctx, FILE_RESUME_BLOCK);
else
- store_verdict(flow, file, block_timeout);
- return true;
+ store_verdict(flow, file_ctx, block_timeout);
+ return true;
}
FileVerdict FileCache::cached_verdict_lookup(Packet* p, FileInfo* file,
struct FileNode
{
- time_t expires;
+ struct timeval cache_expire_time = {0, 0};
snort::FileContext* file;
};
snort::FileContext* get_file(snort::Flow*, uint64_t file_id, bool to_create);
FileVerdict cached_verdict_lookup(snort::Packet*, snort::FileInfo*,
snort::FilePolicyBase*);
- bool apply_verdict(snort::Packet*, snort::FileInfo*, FileVerdict, bool resume,
+ bool apply_verdict(snort::Packet*, snort::FileContext*, FileVerdict, bool resume,
snort::FilePolicyBase*);
private:
file_signature_enabled = other.file_signature_enabled;
file_capture_enabled = other.file_capture_enabled;
file_state = other.file_state;
+ pending_expire_time = other.pending_expire_time;
// only one copy of file capture
file_capture = nullptr;
}
std::string sha_to_string(const uint8_t* sha256);
void set_file_id(uint64_t index);
uint64_t get_file_id() const;
- FileVerdict verdict = FILE_VERDICT_UNKNOWN;
// Configuration functions
void config_file_type(bool enabled);
FileState get_file_state() { return file_state; }
+ FileVerdict verdict = FILE_VERDICT_UNKNOWN;
+ struct timeval pending_expire_time = {0, 0};
+
protected:
std::string file_name;
bool file_name_set = false;
if ( verdict == DAQ_VERDICT_PASS )
verdict = DAQ_VERDICT_BLOCK;
}
+ else if ( verdict == DAQ_VERDICT_RETRY )
+ {
+ return verdict;
+ }
else if ( p->packet_flags & PKT_RESIZED )
{
// we never increase, only trim, but daq doesn't support resizing wire packet
{
//restart inspection by 3rd party
if (!asd.tp_reinspect_by_initiator && (direction == APP_ID_FROM_INITIATOR) &&
- check_reinspect(p, asd))
+ check_reinspect(p, asd) &&
+ p->packet_flags & PKT_STREAM_ORDER_OK)
{
asd.tp_reinspect_by_initiator = true;
asd.set_session_flags(APPID_SESSION_APP_REINSPECT);
#include <cstdio>
#include <unordered_map>
+#include "detection/ips_context.h"
#include "log/log.h"
#include "log/messages.h"
#include "packet_io/sfdaq.h"
reasons[reason_code] = priority;
}
-void PacketTracer::set_log_file(std::string file)
+void PacketTracer::set_log_file(const std::string& file)
{ log_file = file; }
// template needed for unit tests
CreateTCPFlagString(p.ptrs.tcph, tcpFlags);
if (p.ptrs.tcph->th_flags & TH_ACK)
- PacketTracer::log("Packet: TCP %s, %s, seq %u, ack %u\n", tcpFlags, timestamp,
- p.ptrs.tcph->seq(), p.ptrs.tcph->ack());
+ PacketTracer::log("Packet %" PRIu64 ": TCP %s, %s, seq %u, ack %u, dsize %u%s\n",
+ p.context->packet_number, tcpFlags, timestamp,
+ p.ptrs.tcph->seq(), p.ptrs.tcph->ack(), p.dsize,
+ (p.pkth->flags & DAQ_PKT_FLAG_RETRY_PACKET) ? ", retry pkt" : "");
else
- PacketTracer::log("Packet: TCP %s, %s, seq %u\n", tcpFlags, timestamp, p.ptrs.tcph->seq());
+ PacketTracer::log("Packet %" PRIu64 ": TCP %s, %s, seq %u, dsize %u%s\n",
+ p.context->packet_number, tcpFlags, timestamp, p.ptrs.tcph->seq(),
+ p.dsize,
+ (p.pkth->flags & DAQ_PKT_FLAG_RETRY_PACKET) ? ", retry pkt" : "");
break;
}
{
const char* icmp_str = is_v6 ? "ICMPv6" : "ICMP";
- PacketTracer::log("Packet: %s, %s, Type: %u Code: %u \n", icmp_str, timestamp,
+ PacketTracer::log("Packet %" PRIu64 ": %s, %s, Type: %u Code: %u \n",
+ p.context->packet_number, icmp_str, timestamp,
p.ptrs.icmph->type, p.ptrs.icmph->code);
break;
}
default:
- PacketTracer::log("Packet: %s, %s\n", p.get_type(), timestamp);
+ PacketTracer::log("Packet %" PRIu64 ": %s, %s\n",
+ p.context->packet_number, p.get_type(), timestamp);
break;
}
}
static const int max_buff_size = 2048;
// static functions
- static void set_log_file(std::string);
+ static void set_log_file(const std::string&);
static void thread_init();
static void thread_term();
const char* Active::act_str[Active::ACT_MAX][Active::AST_MAX] =
{
{ "allow", "error", "error", "error" },
+ { "retry", "error", "error", "error" },
{ "drop", "cant_drop", "would_drop", "force_drop" },
{ "block", "cant_block", "would_block", "force_block" },
{ "reset", "cant_reset", "would_reset", "force_reset" },
{
bool retry_queued = false;
- // FIXIT-M may need to confirm this packet is not a retransmit...2.9.x has a check for that
- if ( !p->is_rebuilt() and
- ( active_action == ACT_PASS ) and
- SFDAQ::can_retry() )
+ if ( ( active_action == ACT_PASS ) and SFDAQ::can_retry() )
{
if ( SFDAQ::forwarding_packet(p->pkth) )
{
- active_action = ACT_RETRY;
+ if(p->packet_flags & PKT_RETRANSMIT)
+ active_action = ACT_DROP; // Don't add retransmits to retry queue.
+ else
+ active_action = ACT_RETRY;
retry_queued = true;
}
}
case ACT_RESET:
reset_session(p, force);
break;
+ case ACT_RETRY:
+ if(!daq_retry_packet(p))
+ drop_packet(p, force);
+ break;
default:
break;
}
{ AST_ALLOW, AST_CANT, AST_WOULD, AST_FORCE, AST_MAX };
enum ActiveAction : uint8_t
- { ACT_PASS, ACT_DROP, ACT_BLOCK, ACT_RESET, ACT_RETRY, ACT_MAX };
+ { ACT_PASS, ACT_RETRY, ACT_DROP, ACT_BLOCK, ACT_RESET, ACT_MAX };
public:
static void init(SnortConfig*);
#define PKT_FILE_EVENT_SET 0x00400000
#define PKT_IGNORE 0x00800000 /* this packet should be ignored, based on port */
-#define PKT_UNUSED_FLAGS 0xff000000
+#define PKT_RETRANSMIT 0x01000000 // packet is a re-transmitted pkt.
+#define PKT_UNUSED_FLAGS 0xfe000000
#define PKT_TS_OFFLOADED 0x01
#include "segment_overlap_editor.h"
#include "log/messages.h"
+#include "packet_tracer/packet_tracer.h"
#include "tcp_module.h"
#include "tcp_normalizers.h"
#include "tcp_session.h"
+
+static void set_retransmit_flag(snort::Packet* p)
+{
+ if ( snort::PacketTracer::is_active() )
+ {
+ snort::PacketTracer::log("Packet was retransmitted and %s from the retry queue.\n",
+ (p->pkth->flags & DAQ_PKT_FLAG_RETRY_PACKET) ? "is" : "is not");
+ }
+
+ // Mark the packet as being a re-transmit if it's not from the retry
+ // queue. That way we can avoid adding re-transmitted packets to
+ // the retry queue.
+ if ( !(p->pkth->flags & DAQ_PKT_FLAG_RETRY_PACKET) )
+ p->packet_flags |= PKT_RETRANSMIT;
+}
+
void SegmentOverlapState::init_sos(TcpSession* ssn, ReassemblyPolicy pol)
{
// in one segment.
bool* pb = (trs.sos.rseq == trs.sos.tsd->get_seg_seq()) ? full_retransmit : nullptr;
- if ( trs.sos.right->is_retransmit(
- trs.sos.rdata, trs.sos.rsize, trs.sos.rseq, trs.sos.right->i_len, pb) )
+ if ( trs.sos.right->is_retransmit(trs.sos.rdata, trs.sos.rsize,
+ trs.sos.rseq, trs.sos.right->i_len, pb) )
{
+ set_retransmit_flag(trs.sos.tsd->get_pkt());
+
if ( !(*full_retransmit) )
{
trs.sos.rdata += trs.sos.right->i_len;
if ( trs.sos.overlap < trs.sos.right->i_len )
{
- if ( trs.sos.right->is_retransmit(
- trs.sos.rdata, trs.sos.rsize, trs.sos.rseq, trs.sos.right->i_len, nullptr) )
+ if ( trs.sos.right->is_retransmit(trs.sos.rdata, trs.sos.rsize,
+ trs.sos.rseq, trs.sos.right->i_len, nullptr) )
{
+ set_retransmit_flag(trs.sos.tsd->get_pkt());
+
// All data was retransmitted
trs.sos.session->retransmit_process(trs.sos.tsd->get_pkt());
trs.sos.keep_segment = false;
{
if ( ( (int)( ( tsd.get_ts() - tns.peer_tracker->get_ts_last() ) + tns.paws_ts_fudge ) ) < 0 )
{
- /* bail, we've got a packet outside the PAWS window! */
- //inc_tcp_discards();
- tns.session->tel.set_tcp_event(EVENT_BAD_TIMESTAMP);
- packet_dropper(tns, tsd, NORM_TCP_OPT);
- return ACTION_BAD_PKT;
+ if ( tsd.get_pkt()->pkth->flags & DAQ_PKT_FLAG_RETRY_PACKET )
+ {
+ // Retry packets can legitimately have old timestamps
+ // in TCP options (if a re-transmit comes in before
+ // the retry) so don't consider it an error.
+ tsd.set_ts(tns.peer_tracker->get_ts_last());
+ return ACTION_NOTHING;
+ }
+ else
+ {
+ /* bail, we've got a packet outside the PAWS window! */
+ //inc_tcp_discards();
+ tns.session->tel.set_tcp_event(EVENT_BAD_TIMESTAMP);
+ packet_dropper(tns, tsd, NORM_TCP_OPT);
+ return ACTION_BAD_PKT;
+ }
}
else if ( ( tns.peer_tracker->get_ts_last() != 0 )
&& ( ( uint32_t )tsd.get_pkt()->pkth->ts.tv_sec > tns.peer_tracker->get_ts_last_packet() +
tcpStats.segs_released++;
}
-bool TcpSegmentNode::is_retransmit(
- const uint8_t* rdata, uint16_t rsize, uint32_t rseq, uint16_t orig_dsize,
- bool *full_retransmit)
+bool TcpSegmentNode::is_retransmit(const uint8_t* rdata, uint16_t rsize,
+ uint32_t rseq, uint16_t orig_dsize, bool *full_retransmit)
{
// retransmit must have same payload at same place
if ( !SEQ_EQ(i_seq, rseq) )
return false;
- if( orig_dsize == c_len )
+ if ( orig_dsize == c_len )
{
if ( ( ( c_len <= rsize )and !memcmp(data, rdata, c_len) )
or ( ( c_len > rsize )and !memcmp(data, rdata, rsize) ) )
}
//Checking for a possible split of segment in which case
//we compare complete data of the segment to find a retransmission
- else if(full_retransmit and (orig_dsize == rsize) and !memcmp(data, rdata, rsize) )
+ else if ( (orig_dsize == rsize) and !memcmp(data, rdata, rsize) )
{
- *full_retransmit = true;
+ if ( full_retransmit )
+ *full_retransmit = true;
return true;
}
static const char* get_action(uint8_t act)
{
- const char* acts[] = { "pass", "dtop", "block", "reset" };
+ const char* acts[] = { "pass", "retry", "drop", "block", "reset" };
return lookup(acts, sizeof(acts)/sizeof(acts[0]), act);
}