mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
Reformat Zeek in Spicy style
This largely copies over Spicy's `.clang-format` configuration file. The one place where we deviate is header include order since Zeek depends on headers being included in a certain order.
This commit is contained in:
parent
7b8e7ed72c
commit
f5a76c1aed
786 changed files with 131714 additions and 153609 deletions
|
@ -14,149 +14,127 @@
|
|||
#include "zeek/util.h"
|
||||
|
||||
#ifdef DONT_HAVE_LIBPCAP_PCAP_FREECODE
|
||||
extern "C"
|
||||
{
|
||||
extern "C" {
|
||||
#include <pcap-int.h>
|
||||
|
||||
int pcap_freecode(pcap_t* unused, struct bpf_program* program)
|
||||
{
|
||||
program->bf_len = 0;
|
||||
int pcap_freecode(pcap_t* unused, struct bpf_program* program) {
|
||||
program->bf_len = 0;
|
||||
|
||||
if ( program->bf_insns )
|
||||
{
|
||||
free((char*)program->bf_insns); // copied from libpcap
|
||||
program->bf_insns = 0;
|
||||
}
|
||||
if ( program->bf_insns ) {
|
||||
free((char*)program->bf_insns); // copied from libpcap
|
||||
program->bf_insns = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
pcap_t* pcap_open_dead(int linktype, int snaplen)
|
||||
{
|
||||
pcap_t* p;
|
||||
pcap_t* pcap_open_dead(int linktype, int snaplen) {
|
||||
pcap_t* p;
|
||||
|
||||
p = (pcap_t*)malloc(sizeof(*p));
|
||||
if ( ! p )
|
||||
return 0;
|
||||
p = (pcap_t*)malloc(sizeof(*p));
|
||||
if ( ! p )
|
||||
return 0;
|
||||
|
||||
memset(p, 0, sizeof(*p));
|
||||
memset(p, 0, sizeof(*p));
|
||||
|
||||
p->fd = -1;
|
||||
p->snapshot = snaplen;
|
||||
p->linktype = linktype;
|
||||
p->fd = -1;
|
||||
p->snapshot = snaplen;
|
||||
p->linktype = linktype;
|
||||
|
||||
return p;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
int pcap_compile_nopcap(int snaplen_arg, int linktype_arg, struct bpf_program* program,
|
||||
char* buf, int optimize, bpf_u_int32 mask)
|
||||
{
|
||||
pcap_t* p;
|
||||
int ret;
|
||||
int pcap_compile_nopcap(int snaplen_arg, int linktype_arg, struct bpf_program* program, char* buf, int optimize,
|
||||
bpf_u_int32 mask) {
|
||||
pcap_t* p;
|
||||
int ret;
|
||||
|
||||
p = pcap_open_dead(linktype_arg, snaplen_arg);
|
||||
if ( ! p )
|
||||
return -1;
|
||||
p = pcap_open_dead(linktype_arg, snaplen_arg);
|
||||
if ( ! p )
|
||||
return -1;
|
||||
|
||||
ret = pcap_compile(p, program, buf, optimize, mask);
|
||||
pcap_close(p);
|
||||
ret = pcap_compile(p, program, buf, optimize, mask);
|
||||
pcap_close(p);
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace zeek::iosource::detail
|
||||
{
|
||||
namespace zeek::iosource::detail {
|
||||
|
||||
// Simple heuristic to identify filters that always match, so that we can
|
||||
// skip the filtering in that case. "ip or not ip" is Zeek's default filter.
|
||||
static bool filter_matches_anything(const char* filter)
|
||||
{
|
||||
return (! filter) || strlen(filter) == 0 || strcmp(filter, "ip or not ip") == 0;
|
||||
}
|
||||
static bool filter_matches_anything(const char* filter) {
|
||||
return (! filter) || strlen(filter) == 0 || strcmp(filter, "ip or not ip") == 0;
|
||||
}
|
||||
|
||||
BPF_Program::BPF_Program() : m_program() { }
|
||||
BPF_Program::BPF_Program() : m_program() {}
|
||||
|
||||
BPF_Program::~BPF_Program()
|
||||
{
|
||||
FreeCode();
|
||||
}
|
||||
BPF_Program::~BPF_Program() { FreeCode(); }
|
||||
|
||||
bool BPF_Program::Compile(pcap_t* pcap, const char* filter, uint32_t netmask, bool optimize)
|
||||
{
|
||||
if ( ! pcap )
|
||||
return false;
|
||||
bool BPF_Program::Compile(pcap_t* pcap, const char* filter, uint32_t netmask, bool optimize) {
|
||||
if ( ! pcap )
|
||||
return false;
|
||||
|
||||
FreeCode();
|
||||
FreeCode();
|
||||
|
||||
if ( pcap_compile(pcap, &m_program, (char*)filter, optimize, netmask) < 0 )
|
||||
{
|
||||
state_message = std::string(pcap_geterr(pcap));
|
||||
state = GetStateFromMessage(state_message);
|
||||
return false;
|
||||
}
|
||||
if ( pcap_compile(pcap, &m_program, (char*)filter, optimize, netmask) < 0 ) {
|
||||
state_message = std::string(pcap_geterr(pcap));
|
||||
state = GetStateFromMessage(state_message);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_compiled = true;
|
||||
m_matches_anything = filter_matches_anything(filter);
|
||||
m_compiled = true;
|
||||
m_matches_anything = filter_matches_anything(filter);
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BPF_Program::Compile(zeek_uint_t snaplen, int linktype, const char* filter, uint32_t netmask,
|
||||
bool optimize)
|
||||
{
|
||||
FreeCode();
|
||||
bool BPF_Program::Compile(zeek_uint_t snaplen, int linktype, const char* filter, uint32_t netmask, bool optimize) {
|
||||
FreeCode();
|
||||
|
||||
if ( linktype == DLT_NFLOG )
|
||||
{
|
||||
// No-op, NFLOG does not support BPF filters.
|
||||
// Raising a warning might be good, but it would also be noisy
|
||||
// since the default scripts will always attempt to compile
|
||||
// and install a default filter
|
||||
m_compiled = true;
|
||||
m_matches_anything = true;
|
||||
return true;
|
||||
}
|
||||
if ( linktype == DLT_NFLOG ) {
|
||||
// No-op, NFLOG does not support BPF filters.
|
||||
// Raising a warning might be good, but it would also be noisy
|
||||
// since the default scripts will always attempt to compile
|
||||
// and install a default filter
|
||||
m_compiled = true;
|
||||
m_matches_anything = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
pcap_t* pcap = pcap_open_dead(linktype, snaplen);
|
||||
if ( ! pcap )
|
||||
{
|
||||
state = FilterState::FATAL;
|
||||
state_message = "Failed to open pcap based on linktype/snaplen";
|
||||
return false;
|
||||
}
|
||||
pcap_t* pcap = pcap_open_dead(linktype, snaplen);
|
||||
if ( ! pcap ) {
|
||||
state = FilterState::FATAL;
|
||||
state_message = "Failed to open pcap based on linktype/snaplen";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool status = Compile(pcap, filter, netmask, optimize);
|
||||
pcap_close(pcap);
|
||||
bool status = Compile(pcap, filter, netmask, optimize);
|
||||
pcap_close(pcap);
|
||||
|
||||
return status;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
bpf_program* BPF_Program::GetProgram()
|
||||
{
|
||||
return m_compiled ? &m_program : nullptr;
|
||||
}
|
||||
bpf_program* BPF_Program::GetProgram() { return m_compiled ? &m_program : nullptr; }
|
||||
|
||||
void BPF_Program::FreeCode()
|
||||
{
|
||||
if ( m_compiled )
|
||||
{
|
||||
void BPF_Program::FreeCode() {
|
||||
if ( m_compiled ) {
|
||||
#ifdef DONT_HAVE_LIBPCAP_PCAP_FREECODE
|
||||
pcap_freecode(NULL, &m_program);
|
||||
pcap_freecode(NULL, &m_program);
|
||||
#else
|
||||
pcap_freecode(&m_program);
|
||||
pcap_freecode(&m_program);
|
||||
#endif
|
||||
m_compiled = false;
|
||||
}
|
||||
}
|
||||
m_compiled = false;
|
||||
}
|
||||
}
|
||||
|
||||
FilterState BPF_Program::GetStateFromMessage(const std::string& err)
|
||||
{
|
||||
if ( err.find("filtering not implemented") != std::string::npos )
|
||||
return FilterState::WARNING;
|
||||
FilterState BPF_Program::GetStateFromMessage(const std::string& err) {
|
||||
if ( err.find("filtering not implemented") != std::string::npos )
|
||||
return FilterState::WARNING;
|
||||
|
||||
return FilterState::FATAL;
|
||||
}
|
||||
return FilterState::FATAL;
|
||||
}
|
||||
|
||||
} // namespace zeek::iosource::detail
|
||||
} // namespace zeek::iosource::detail
|
||||
|
|
|
@ -7,94 +7,88 @@
|
|||
|
||||
#include "zeek/util.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
extern "C" {
|
||||
#include <pcap.h>
|
||||
}
|
||||
}
|
||||
|
||||
namespace zeek::iosource
|
||||
{
|
||||
namespace zeek::iosource {
|
||||
|
||||
enum class FilterState : uint8_t
|
||||
{
|
||||
OK,
|
||||
FATAL, // results in Reporter::Error
|
||||
WARNING // results in Reporter::Warning
|
||||
};
|
||||
enum class FilterState : uint8_t {
|
||||
OK,
|
||||
FATAL, // results in Reporter::Error
|
||||
WARNING // results in Reporter::Warning
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
namespace detail {
|
||||
|
||||
// BPF_Programs are an abstraction around struct bpf_program,
|
||||
// to create a clean facility for creating, compiling, and
|
||||
// freeing such programs.
|
||||
|
||||
class BPF_Program
|
||||
{
|
||||
class BPF_Program {
|
||||
public:
|
||||
/**
|
||||
* Creates an empty, uncompiled BPF program.
|
||||
*/
|
||||
BPF_Program();
|
||||
~BPF_Program();
|
||||
/**
|
||||
* Creates an empty, uncompiled BPF program.
|
||||
*/
|
||||
BPF_Program();
|
||||
~BPF_Program();
|
||||
|
||||
/**
|
||||
* Creates a BPF program for a given pcap handle. The parameters match the usage
|
||||
* described in the documentation for pcap_compile().
|
||||
*
|
||||
* @return true on successful compilation, false otherwise.
|
||||
*/
|
||||
bool Compile(pcap_t* pcap, const char* filter, uint32_t netmask, bool optimize = true);
|
||||
/**
|
||||
* Creates a BPF program for a given pcap handle. The parameters match the usage
|
||||
* described in the documentation for pcap_compile().
|
||||
*
|
||||
* @return true on successful compilation, false otherwise.
|
||||
*/
|
||||
bool Compile(pcap_t* pcap, const char* filter, uint32_t netmask, bool optimize = true);
|
||||
|
||||
/**
|
||||
* Creates a BPF program when no pcap handle is available. The parameters match the usage
|
||||
* described in the documentation for pcap_compile_nopcap().
|
||||
*
|
||||
* @return true on successful compilation, false otherwise.
|
||||
*/
|
||||
bool Compile(zeek_uint_t snaplen, int linktype, const char* filter, uint32_t netmask,
|
||||
bool optimize = true);
|
||||
/**
|
||||
* Creates a BPF program when no pcap handle is available. The parameters match the usage
|
||||
* described in the documentation for pcap_compile_nopcap().
|
||||
*
|
||||
* @return true on successful compilation, false otherwise.
|
||||
*/
|
||||
bool Compile(zeek_uint_t snaplen, int linktype, const char* filter, uint32_t netmask, bool optimize = true);
|
||||
|
||||
/**
|
||||
* Returns true if this program currently contains compiled code, false otherwise.
|
||||
*/
|
||||
bool IsCompiled() { return m_compiled; }
|
||||
/**
|
||||
* Returns true if this program currently contains compiled code, false otherwise.
|
||||
*/
|
||||
bool IsCompiled() { return m_compiled; }
|
||||
|
||||
/**
|
||||
* Returns true if this program matches any packets. This is not comprehensive, but can
|
||||
* identify a few cases where it does.
|
||||
*/
|
||||
bool MatchesAnything() { return m_matches_anything; }
|
||||
/**
|
||||
* Returns true if this program matches any packets. This is not comprehensive, but can
|
||||
* identify a few cases where it does.
|
||||
*/
|
||||
bool MatchesAnything() { return m_matches_anything; }
|
||||
|
||||
/**
|
||||
* Returns the compiled program, or nullptr if no program is currently compiled.
|
||||
*/
|
||||
bpf_program* GetProgram();
|
||||
/**
|
||||
* Returns the compiled program, or nullptr if no program is currently compiled.
|
||||
*/
|
||||
bpf_program* GetProgram();
|
||||
|
||||
/**
|
||||
* Returns the state of the compilation process.
|
||||
*/
|
||||
FilterState GetState() const { return state; }
|
||||
/**
|
||||
* Returns the state of the compilation process.
|
||||
*/
|
||||
FilterState GetState() const { return state; }
|
||||
|
||||
/**
|
||||
* Returns an error message, if any, that was returned from the compilation process.
|
||||
*/
|
||||
std::string GetStateMessage() const { return state_message; }
|
||||
/**
|
||||
* Returns an error message, if any, that was returned from the compilation process.
|
||||
*/
|
||||
std::string GetStateMessage() const { return state_message; }
|
||||
|
||||
protected:
|
||||
void FreeCode();
|
||||
void FreeCode();
|
||||
|
||||
FilterState GetStateFromMessage(const std::string& err);
|
||||
FilterState GetStateFromMessage(const std::string& err);
|
||||
|
||||
// (I like to prefix member variables with m_, makes it clear
|
||||
// in the implementation whether it's a global or not. --ck)
|
||||
bool m_compiled = false;
|
||||
bool m_matches_anything = false;
|
||||
struct bpf_program m_program;
|
||||
// (I like to prefix member variables with m_, makes it clear
|
||||
// in the implementation whether it's a global or not. --ck)
|
||||
bool m_compiled = false;
|
||||
bool m_matches_anything = false;
|
||||
struct bpf_program m_program;
|
||||
|
||||
FilterState state = FilterState::OK;
|
||||
std::string state_message;
|
||||
};
|
||||
FilterState state = FilterState::OK;
|
||||
std::string state_message;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace zeek::iosource
|
||||
} // namespace detail
|
||||
} // namespace zeek::iosource
|
||||
|
|
|
@ -5,149 +5,107 @@
|
|||
#include "zeek/Desc.h"
|
||||
#include "zeek/Reporter.h"
|
||||
|
||||
namespace zeek::iosource
|
||||
{
|
||||
namespace zeek::iosource {
|
||||
|
||||
Component::Component(const std::string& name) : plugin::Component(plugin::component::IOSOURCE, name)
|
||||
{
|
||||
}
|
||||
Component::Component(const std::string& name) : plugin::Component(plugin::component::IOSOURCE, name) {}
|
||||
|
||||
Component::Component(plugin::component::Type type, const std::string& name)
|
||||
: plugin::Component(type, name)
|
||||
{
|
||||
}
|
||||
Component::Component(plugin::component::Type type, const std::string& name) : plugin::Component(type, name) {}
|
||||
|
||||
PktSrcComponent::PktSrcComponent(const std::string& arg_name, const std::string& arg_prefix,
|
||||
InputType arg_type, factory_callback arg_factory)
|
||||
: Component(plugin::component::PKTSRC, arg_name)
|
||||
{
|
||||
util::tokenize_string(arg_prefix, ":", &prefixes);
|
||||
type = arg_type;
|
||||
factory = arg_factory;
|
||||
}
|
||||
PktSrcComponent::PktSrcComponent(const std::string& arg_name, const std::string& arg_prefix, InputType arg_type,
|
||||
factory_callback arg_factory)
|
||||
: Component(plugin::component::PKTSRC, arg_name) {
|
||||
util::tokenize_string(arg_prefix, ":", &prefixes);
|
||||
type = arg_type;
|
||||
factory = arg_factory;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& PktSrcComponent::Prefixes() const
|
||||
{
|
||||
return prefixes;
|
||||
}
|
||||
const std::vector<std::string>& PktSrcComponent::Prefixes() const { return prefixes; }
|
||||
|
||||
bool PktSrcComponent::HandlesPrefix(const std::string& prefix) const
|
||||
{
|
||||
for ( std::vector<std::string>::const_iterator i = prefixes.begin(); i != prefixes.end(); i++ )
|
||||
{
|
||||
if ( *i == prefix )
|
||||
return true;
|
||||
}
|
||||
bool PktSrcComponent::HandlesPrefix(const std::string& prefix) const {
|
||||
for ( std::vector<std::string>::const_iterator i = prefixes.begin(); i != prefixes.end(); i++ ) {
|
||||
if ( *i == prefix )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PktSrcComponent::DoesLive() const
|
||||
{
|
||||
return type == LIVE || type == BOTH;
|
||||
}
|
||||
bool PktSrcComponent::DoesLive() const { return type == LIVE || type == BOTH; }
|
||||
|
||||
bool PktSrcComponent::DoesTrace() const
|
||||
{
|
||||
return type == TRACE || type == BOTH;
|
||||
}
|
||||
bool PktSrcComponent::DoesTrace() const { return type == TRACE || type == BOTH; }
|
||||
|
||||
PktSrcComponent::factory_callback PktSrcComponent::Factory() const
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
PktSrcComponent::factory_callback PktSrcComponent::Factory() const { return factory; }
|
||||
|
||||
void PktSrcComponent::DoDescribe(ODesc* d) const
|
||||
{
|
||||
Component::DoDescribe(d);
|
||||
void PktSrcComponent::DoDescribe(ODesc* d) const {
|
||||
Component::DoDescribe(d);
|
||||
|
||||
std::string prefs;
|
||||
std::string prefs;
|
||||
|
||||
for ( std::vector<std::string>::const_iterator i = prefixes.begin(); i != prefixes.end(); i++ )
|
||||
{
|
||||
if ( prefs.size() )
|
||||
prefs += ", ";
|
||||
for ( std::vector<std::string>::const_iterator i = prefixes.begin(); i != prefixes.end(); i++ ) {
|
||||
if ( prefs.size() )
|
||||
prefs += ", ";
|
||||
|
||||
prefs += '"' + *i + '"';
|
||||
}
|
||||
prefs += '"' + *i + '"';
|
||||
}
|
||||
|
||||
d->Add("interface prefix");
|
||||
if ( prefixes.size() > 1 )
|
||||
d->Add("es");
|
||||
d->Add("interface prefix");
|
||||
if ( prefixes.size() > 1 )
|
||||
d->Add("es");
|
||||
|
||||
d->Add(" ");
|
||||
d->Add(prefs);
|
||||
d->Add("; supports ");
|
||||
d->Add(" ");
|
||||
d->Add(prefs);
|
||||
d->Add("; supports ");
|
||||
|
||||
switch ( type )
|
||||
{
|
||||
case LIVE:
|
||||
d->Add("live input");
|
||||
break;
|
||||
switch ( type ) {
|
||||
case LIVE: d->Add("live input"); break;
|
||||
|
||||
case TRACE:
|
||||
d->Add("trace input");
|
||||
break;
|
||||
case TRACE: d->Add("trace input"); break;
|
||||
|
||||
case BOTH:
|
||||
d->Add("live and trace input");
|
||||
break;
|
||||
case BOTH: d->Add("live and trace input"); break;
|
||||
|
||||
default:
|
||||
reporter->InternalError("unknown PkrSrc type");
|
||||
}
|
||||
}
|
||||
default: reporter->InternalError("unknown PkrSrc type");
|
||||
}
|
||||
}
|
||||
|
||||
PktDumperComponent::PktDumperComponent(const std::string& name, const std::string& arg_prefix,
|
||||
factory_callback arg_factory)
|
||||
: plugin::Component(plugin::component::PKTDUMPER, name)
|
||||
{
|
||||
util::tokenize_string(arg_prefix, ":", &prefixes);
|
||||
factory = arg_factory;
|
||||
}
|
||||
: plugin::Component(plugin::component::PKTDUMPER, name) {
|
||||
util::tokenize_string(arg_prefix, ":", &prefixes);
|
||||
factory = arg_factory;
|
||||
}
|
||||
|
||||
PktDumperComponent::factory_callback PktDumperComponent::Factory() const
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
PktDumperComponent::factory_callback PktDumperComponent::Factory() const { return factory; }
|
||||
|
||||
const std::vector<std::string>& PktDumperComponent::Prefixes() const
|
||||
{
|
||||
return prefixes;
|
||||
}
|
||||
const std::vector<std::string>& PktDumperComponent::Prefixes() const { return prefixes; }
|
||||
|
||||
bool PktDumperComponent::HandlesPrefix(const std::string& prefix) const
|
||||
{
|
||||
for ( std::vector<std::string>::const_iterator i = prefixes.begin(); i != prefixes.end(); i++ )
|
||||
{
|
||||
if ( *i == prefix )
|
||||
return true;
|
||||
}
|
||||
bool PktDumperComponent::HandlesPrefix(const std::string& prefix) const {
|
||||
for ( std::vector<std::string>::const_iterator i = prefixes.begin(); i != prefixes.end(); i++ ) {
|
||||
if ( *i == prefix )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PktDumperComponent::DoDescribe(ODesc* d) const
|
||||
{
|
||||
plugin::Component::DoDescribe(d);
|
||||
void PktDumperComponent::DoDescribe(ODesc* d) const {
|
||||
plugin::Component::DoDescribe(d);
|
||||
|
||||
std::string prefs;
|
||||
std::string prefs;
|
||||
|
||||
for ( std::vector<std::string>::const_iterator i = prefixes.begin(); i != prefixes.end(); i++ )
|
||||
{
|
||||
if ( prefs.size() )
|
||||
prefs += ", ";
|
||||
for ( std::vector<std::string>::const_iterator i = prefixes.begin(); i != prefixes.end(); i++ ) {
|
||||
if ( prefs.size() )
|
||||
prefs += ", ";
|
||||
|
||||
prefs += '"' + *i + '"';
|
||||
}
|
||||
prefs += '"' + *i + '"';
|
||||
}
|
||||
|
||||
d->Add("dumper prefix");
|
||||
d->Add("dumper prefix");
|
||||
|
||||
if ( prefixes.size() > 1 )
|
||||
d->Add("es");
|
||||
if ( prefixes.size() > 1 )
|
||||
d->Add("es");
|
||||
|
||||
d->Add(": ");
|
||||
d->Add(prefs);
|
||||
}
|
||||
d->Add(": ");
|
||||
d->Add(prefs);
|
||||
}
|
||||
|
||||
} // namespace zeek::iosource
|
||||
} // namespace zeek::iosource
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
|
||||
#include "zeek/plugin/Component.h"
|
||||
|
||||
namespace zeek::iosource
|
||||
{
|
||||
namespace zeek::iosource {
|
||||
|
||||
class IOSource;
|
||||
class PktSrc;
|
||||
|
@ -17,113 +16,109 @@ class PktDumper;
|
|||
/**
|
||||
* Component description for plugins providing IOSources.
|
||||
*/
|
||||
class Component : public plugin::Component
|
||||
{
|
||||
class Component : public plugin::Component {
|
||||
public:
|
||||
using factory_callback = IOSource* (*)();
|
||||
using factory_callback = IOSource* (*)();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param name A descriptive name for the component. This name must
|
||||
* be unique across all components of this type.
|
||||
*/
|
||||
explicit Component(const std::string& name);
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param name A descriptive name for the component. This name must
|
||||
* be unique across all components of this type.
|
||||
*/
|
||||
explicit Component(const std::string& name);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~Component() override = default;
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~Component() override = default;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructor to use by derived classes.
|
||||
*
|
||||
* @param type The type of the component.
|
||||
*
|
||||
* @param name A descriptive name for the component. This name must
|
||||
* be unique across all components of this type.
|
||||
*/
|
||||
Component(plugin::component::Type type, const std::string& name);
|
||||
};
|
||||
/**
|
||||
* Constructor to use by derived classes.
|
||||
*
|
||||
* @param type The type of the component.
|
||||
*
|
||||
* @param name A descriptive name for the component. This name must
|
||||
* be unique across all components of this type.
|
||||
*/
|
||||
Component(plugin::component::Type type, const std::string& name);
|
||||
};
|
||||
|
||||
/**
|
||||
* Component description for plugins providing a PktSrc for packet input.
|
||||
*/
|
||||
class PktSrcComponent : public Component
|
||||
{
|
||||
class PktSrcComponent : public Component {
|
||||
public:
|
||||
/**
|
||||
* Type of input a packet source supports.
|
||||
*/
|
||||
enum InputType
|
||||
{
|
||||
LIVE, ///< Live input.
|
||||
TRACE, ///< Offline input from trace file.
|
||||
BOTH ///< Live input as well as offline.
|
||||
};
|
||||
/**
|
||||
* Type of input a packet source supports.
|
||||
*/
|
||||
enum InputType {
|
||||
LIVE, ///< Live input.
|
||||
TRACE, ///< Offline input from trace file.
|
||||
BOTH ///< Live input as well as offline.
|
||||
};
|
||||
|
||||
using factory_callback = PktSrc* (*)(const std::string& path, bool is_live);
|
||||
using factory_callback = PktSrc* (*)(const std::string& path, bool is_live);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param name A descriptive name for the component. This name must
|
||||
* be unique across all components of this type.
|
||||
*
|
||||
* @param prefixes The list of interface/file prefixes associated
|
||||
* with this component.
|
||||
*
|
||||
* @param type Type of input the component supports.
|
||||
*
|
||||
* @param factor Factory function to instantiate component.
|
||||
*/
|
||||
PktSrcComponent(const std::string& name, const std::string& prefixes, InputType type,
|
||||
factory_callback factory);
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param name A descriptive name for the component. This name must
|
||||
* be unique across all components of this type.
|
||||
*
|
||||
* @param prefixes The list of interface/file prefixes associated
|
||||
* with this component.
|
||||
*
|
||||
* @param type Type of input the component supports.
|
||||
*
|
||||
* @param factor Factory function to instantiate component.
|
||||
*/
|
||||
PktSrcComponent(const std::string& name, const std::string& prefixes, InputType type, factory_callback factory);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~PktSrcComponent() override = default;
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~PktSrcComponent() override = default;
|
||||
|
||||
/**
|
||||
* Returns the prefix(es) passed to the constructor.
|
||||
*/
|
||||
const std::vector<std::string>& Prefixes() const;
|
||||
/**
|
||||
* Returns the prefix(es) passed to the constructor.
|
||||
*/
|
||||
const std::vector<std::string>& Prefixes() const;
|
||||
|
||||
/**
|
||||
* Returns true if the given prefix is among the one specified for the component.
|
||||
*/
|
||||
bool HandlesPrefix(const std::string& prefix) const;
|
||||
/**
|
||||
* Returns true if the given prefix is among the one specified for the component.
|
||||
*/
|
||||
bool HandlesPrefix(const std::string& prefix) const;
|
||||
|
||||
/**
|
||||
* Returns true if packet source instantiated by the component handle
|
||||
* live traffic.
|
||||
*/
|
||||
bool DoesLive() const;
|
||||
/**
|
||||
* Returns true if packet source instantiated by the component handle
|
||||
* live traffic.
|
||||
*/
|
||||
bool DoesLive() const;
|
||||
|
||||
/**
|
||||
* Returns true if packet source instantiated by the component handle
|
||||
* offline traces.
|
||||
*/
|
||||
bool DoesTrace() const;
|
||||
/**
|
||||
* Returns true if packet source instantiated by the component handle
|
||||
* offline traces.
|
||||
*/
|
||||
bool DoesTrace() const;
|
||||
|
||||
/**
|
||||
* Returns the source's factory function.
|
||||
*/
|
||||
factory_callback Factory() const;
|
||||
/**
|
||||
* Returns the source's factory function.
|
||||
*/
|
||||
factory_callback Factory() const;
|
||||
|
||||
/**
|
||||
* Generates a human-readable description of the component. This goes
|
||||
* into the output of \c "zeek -NN".
|
||||
*/
|
||||
void DoDescribe(ODesc* d) const override;
|
||||
/**
|
||||
* Generates a human-readable description of the component. This goes
|
||||
* into the output of \c "zeek -NN".
|
||||
*/
|
||||
void DoDescribe(ODesc* d) const override;
|
||||
|
||||
private:
|
||||
std::vector<std::string> prefixes;
|
||||
InputType type;
|
||||
factory_callback factory;
|
||||
};
|
||||
std::vector<std::string> prefixes;
|
||||
InputType type;
|
||||
factory_callback factory;
|
||||
};
|
||||
|
||||
/**
|
||||
* Component description for plugins providing a PktDumper for packet output.
|
||||
|
@ -131,46 +126,44 @@ private:
|
|||
* PktDumpers aren't IOSources but we locate them here to keep them along with
|
||||
* the PktSrc.
|
||||
*/
|
||||
class PktDumperComponent : public plugin::Component
|
||||
{
|
||||
class PktDumperComponent : public plugin::Component {
|
||||
public:
|
||||
using factory_callback = PktDumper* (*)(const std::string& path, bool append);
|
||||
using factory_callback = PktDumper* (*)(const std::string& path, bool append);
|
||||
|
||||
/**
|
||||
* XXX
|
||||
*/
|
||||
PktDumperComponent(const std::string& name, const std::string& prefixes,
|
||||
factory_callback factory);
|
||||
/**
|
||||
* XXX
|
||||
*/
|
||||
PktDumperComponent(const std::string& name, const std::string& prefixes, factory_callback factory);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~PktDumperComponent() override = default;
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~PktDumperComponent() override = default;
|
||||
|
||||
/**
|
||||
* Returns the prefix(es) passed to the constructor.
|
||||
*/
|
||||
const std::vector<std::string>& Prefixes() const;
|
||||
/**
|
||||
* Returns the prefix(es) passed to the constructor.
|
||||
*/
|
||||
const std::vector<std::string>& Prefixes() const;
|
||||
|
||||
/**
|
||||
* Returns true if the given prefix is among the one specified for the component.
|
||||
*/
|
||||
bool HandlesPrefix(const std::string& prefix) const;
|
||||
/**
|
||||
* Returns true if the given prefix is among the one specified for the component.
|
||||
*/
|
||||
bool HandlesPrefix(const std::string& prefix) const;
|
||||
|
||||
/**
|
||||
* Returns the source's factory function.
|
||||
*/
|
||||
factory_callback Factory() const;
|
||||
/**
|
||||
* Returns the source's factory function.
|
||||
*/
|
||||
factory_callback Factory() const;
|
||||
|
||||
/**
|
||||
* Generates a human-readable description of the component. This goes
|
||||
* into the output of \c "zeek -NN".
|
||||
*/
|
||||
void DoDescribe(ODesc* d) const override;
|
||||
/**
|
||||
* Generates a human-readable description of the component. This goes
|
||||
* into the output of \c "zeek -NN".
|
||||
*/
|
||||
void DoDescribe(ODesc* d) const override;
|
||||
|
||||
private:
|
||||
std::vector<std::string> prefixes;
|
||||
factory_callback factory;
|
||||
};
|
||||
std::vector<std::string> prefixes;
|
||||
factory_callback factory;
|
||||
};
|
||||
|
||||
} // namespace zeek::iosource
|
||||
} // namespace zeek::iosource
|
||||
|
|
|
@ -2,113 +2,107 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
namespace zeek::iosource
|
||||
{
|
||||
namespace zeek::iosource {
|
||||
|
||||
/**
|
||||
* Interface class for components providing/consuming data inside Zeek's main
|
||||
* loop.
|
||||
*/
|
||||
class IOSource
|
||||
{
|
||||
class IOSource {
|
||||
public:
|
||||
enum ProcessFlags
|
||||
{
|
||||
READ = 0x01,
|
||||
WRITE = 0x02
|
||||
};
|
||||
enum ProcessFlags { READ = 0x01, WRITE = 0x02 };
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param process_fd A flag for indicating whether the child class implements
|
||||
* the ProcessFd() method. This is used by the run loop for dispatching to the
|
||||
* appropriate process method.
|
||||
*/
|
||||
IOSource(bool process_fd = false) : implements_process_fd(process_fd) { }
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param process_fd A flag for indicating whether the child class implements
|
||||
* the ProcessFd() method. This is used by the run loop for dispatching to the
|
||||
* appropriate process method.
|
||||
*/
|
||||
IOSource(bool process_fd = false) : implements_process_fd(process_fd) {}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~IOSource() { }
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~IOSource() {}
|
||||
|
||||
/**
|
||||
* Returns true if more data is to be expected in the future.
|
||||
* Otherwise, source may be removed.
|
||||
*/
|
||||
bool IsOpen() const { return ! closed; }
|
||||
/**
|
||||
* Returns true if more data is to be expected in the future.
|
||||
* Otherwise, source may be removed.
|
||||
*/
|
||||
bool IsOpen() const { return ! closed; }
|
||||
|
||||
/**
|
||||
* Returns true if this is a packet source.
|
||||
*/
|
||||
virtual bool IsPacketSource() const { return false; }
|
||||
/**
|
||||
* Returns true if this is a packet source.
|
||||
*/
|
||||
virtual bool IsPacketSource() const { return false; }
|
||||
|
||||
/**
|
||||
* Initializes the source. Can be overwritten by derived classes.
|
||||
*/
|
||||
virtual void InitSource() { }
|
||||
/**
|
||||
* Initializes the source. Can be overwritten by derived classes.
|
||||
*/
|
||||
virtual void InitSource() {}
|
||||
|
||||
/**
|
||||
* Finalizes the source when it's being closed. Can be overwritten by
|
||||
* derived classes.
|
||||
*/
|
||||
virtual void Done() { }
|
||||
/**
|
||||
* Finalizes the source when it's being closed. Can be overwritten by
|
||||
* derived classes.
|
||||
*/
|
||||
virtual void Done() {}
|
||||
|
||||
/**
|
||||
* Return the next timeout value for this source. This should be
|
||||
* overridden by source classes where they have a timeout value
|
||||
* that can wake up the poll.
|
||||
*
|
||||
* Must be overridden by derived classes.
|
||||
*
|
||||
* @return A value for the next time that the source thinks the
|
||||
* poll should time out in seconds from the current time. Return
|
||||
* -1 if this source should not be considered. This should be a
|
||||
* a value relative to network_time, not an absolute time.
|
||||
*/
|
||||
virtual double GetNextTimeout() = 0;
|
||||
/**
|
||||
* Return the next timeout value for this source. This should be
|
||||
* overridden by source classes where they have a timeout value
|
||||
* that can wake up the poll.
|
||||
*
|
||||
* Must be overridden by derived classes.
|
||||
*
|
||||
* @return A value for the next time that the source thinks the
|
||||
* poll should time out in seconds from the current time. Return
|
||||
* -1 if this source should not be considered. This should be a
|
||||
* a value relative to network_time, not an absolute time.
|
||||
*/
|
||||
virtual double GetNextTimeout() = 0;
|
||||
|
||||
/**
|
||||
* Processes and consumes next data item. This will be called by
|
||||
* net_run when this IOSource has been marked ready.
|
||||
*
|
||||
* Must be overridden by derived classes.
|
||||
*/
|
||||
virtual void Process() = 0;
|
||||
/**
|
||||
* Processes and consumes next data item. This will be called by
|
||||
* net_run when this IOSource has been marked ready.
|
||||
*
|
||||
* Must be overridden by derived classes.
|
||||
*/
|
||||
virtual void Process() = 0;
|
||||
|
||||
/**
|
||||
* Optional process method that allows an IOSource to only process
|
||||
* the file descriptor that is found ready and not every possible
|
||||
* descriptor. If this method is implemented, true must be passed
|
||||
* to the IOSource constructor via the child class.
|
||||
*
|
||||
* @param fd The file descriptor to process.
|
||||
* @param flags Flags indicating what type of event is being
|
||||
* processed.
|
||||
*/
|
||||
virtual void ProcessFd(int fd, int flags) { }
|
||||
bool ImplementsProcessFd() const { return implements_process_fd; }
|
||||
/**
|
||||
* Optional process method that allows an IOSource to only process
|
||||
* the file descriptor that is found ready and not every possible
|
||||
* descriptor. If this method is implemented, true must be passed
|
||||
* to the IOSource constructor via the child class.
|
||||
*
|
||||
* @param fd The file descriptor to process.
|
||||
* @param flags Flags indicating what type of event is being
|
||||
* processed.
|
||||
*/
|
||||
virtual void ProcessFd(int fd, int flags) {}
|
||||
bool ImplementsProcessFd() const { return implements_process_fd; }
|
||||
|
||||
/**
|
||||
* Returns a descriptive tag representing the source for debugging.
|
||||
*
|
||||
* Must be overridden by derived classes.
|
||||
*
|
||||
* @return The debugging name.
|
||||
*/
|
||||
virtual const char* Tag() = 0;
|
||||
/**
|
||||
* Returns a descriptive tag representing the source for debugging.
|
||||
*
|
||||
* Must be overridden by derived classes.
|
||||
*
|
||||
* @return The debugging name.
|
||||
*/
|
||||
virtual const char* Tag() = 0;
|
||||
|
||||
protected:
|
||||
/*
|
||||
* Callback for derived class to call when they have shutdown.
|
||||
*
|
||||
* @param is_closed True if the source is now closed.
|
||||
*/
|
||||
void SetClosed(bool is_closed) { closed = is_closed; }
|
||||
/*
|
||||
* Callback for derived class to call when they have shutdown.
|
||||
*
|
||||
* @param is_closed True if the source is now closed.
|
||||
*/
|
||||
void SetClosed(bool is_closed) { closed = is_closed; }
|
||||
|
||||
private:
|
||||
bool closed = false;
|
||||
bool implements_process_fd = false;
|
||||
};
|
||||
bool closed = false;
|
||||
bool implements_process_fd = false;
|
||||
};
|
||||
|
||||
} // namespace zeek::iosource
|
||||
} // namespace zeek::iosource
|
||||
|
|
|
@ -26,489 +26,417 @@
|
|||
|
||||
extern int signal_val;
|
||||
|
||||
namespace zeek::iosource
|
||||
{
|
||||
namespace zeek::iosource {
|
||||
|
||||
Manager::WakeupHandler::WakeupHandler()
|
||||
{
|
||||
if ( ! iosource_mgr->RegisterFd(flare.FD(), this) )
|
||||
reporter->FatalError("Failed to register WakeupHandler's fd with iosource_mgr");
|
||||
}
|
||||
Manager::WakeupHandler::WakeupHandler() {
|
||||
if ( ! iosource_mgr->RegisterFd(flare.FD(), this) )
|
||||
reporter->FatalError("Failed to register WakeupHandler's fd with iosource_mgr");
|
||||
}
|
||||
|
||||
Manager::WakeupHandler::~WakeupHandler()
|
||||
{
|
||||
iosource_mgr->UnregisterFd(flare.FD(), this);
|
||||
}
|
||||
Manager::WakeupHandler::~WakeupHandler() { iosource_mgr->UnregisterFd(flare.FD(), this); }
|
||||
|
||||
void Manager::WakeupHandler::Process()
|
||||
{
|
||||
flare.Extinguish();
|
||||
}
|
||||
void Manager::WakeupHandler::Process() { flare.Extinguish(); }
|
||||
|
||||
void Manager::WakeupHandler::Ping(std::string_view where)
|
||||
{
|
||||
// Calling DBG_LOG calls fprintf, which isn't safe to call in a signal
|
||||
// handler.
|
||||
if ( signal_val != 0 )
|
||||
DBG_LOG(DBG_MAINLOOP, "Pinging WakeupHandler from %s", where.data());
|
||||
void Manager::WakeupHandler::Ping(std::string_view where) {
|
||||
// Calling DBG_LOG calls fprintf, which isn't safe to call in a signal
|
||||
// handler.
|
||||
if ( signal_val != 0 )
|
||||
DBG_LOG(DBG_MAINLOOP, "Pinging WakeupHandler from %s", where.data());
|
||||
|
||||
flare.Fire(true);
|
||||
}
|
||||
flare.Fire(true);
|
||||
}
|
||||
|
||||
Manager::Manager()
|
||||
{
|
||||
event_queue = kqueue();
|
||||
if ( event_queue == -1 )
|
||||
reporter->FatalError("Failed to initialize kqueue: %s", strerror(errno));
|
||||
}
|
||||
Manager::Manager() {
|
||||
event_queue = kqueue();
|
||||
if ( event_queue == -1 )
|
||||
reporter->FatalError("Failed to initialize kqueue: %s", strerror(errno));
|
||||
}
|
||||
|
||||
Manager::~Manager()
|
||||
{
|
||||
delete wakeup;
|
||||
wakeup = nullptr;
|
||||
Manager::~Manager() {
|
||||
delete wakeup;
|
||||
wakeup = nullptr;
|
||||
|
||||
// Make sure all of the sources are done before we try to delete any of them.
|
||||
for ( auto& src : sources )
|
||||
src->src->Done();
|
||||
// Make sure all of the sources are done before we try to delete any of them.
|
||||
for ( auto& src : sources )
|
||||
src->src->Done();
|
||||
|
||||
for ( auto& src : sources )
|
||||
{
|
||||
if ( src->manage_lifetime )
|
||||
delete src->src;
|
||||
for ( auto& src : sources ) {
|
||||
if ( src->manage_lifetime )
|
||||
delete src->src;
|
||||
|
||||
delete src;
|
||||
}
|
||||
delete src;
|
||||
}
|
||||
|
||||
sources.clear();
|
||||
sources.clear();
|
||||
|
||||
for ( PktDumperList::iterator i = pkt_dumpers.begin(); i != pkt_dumpers.end(); ++i )
|
||||
{
|
||||
(*i)->Done();
|
||||
delete *i;
|
||||
}
|
||||
for ( PktDumperList::iterator i = pkt_dumpers.begin(); i != pkt_dumpers.end(); ++i ) {
|
||||
(*i)->Done();
|
||||
delete *i;
|
||||
}
|
||||
|
||||
pkt_dumpers.clear();
|
||||
pkt_dumpers.clear();
|
||||
|
||||
#ifndef _MSC_VER
|
||||
// There's a bug here with builds on Windows that causes an assertion with debug builds
|
||||
// related to libkqueue returning a zero for the file descriptor. The assert happens
|
||||
// because something else has already closed FD zero by the time we get here, and Windows
|
||||
// doesn't like that very much. We only do this close when shutting down, so it should
|
||||
// be fine to just skip it.
|
||||
//
|
||||
// See https://github.com/mheily/libkqueue/issues/151 for more details.
|
||||
if ( event_queue != -1 )
|
||||
close(event_queue);
|
||||
// There's a bug here with builds on Windows that causes an assertion with debug builds
|
||||
// related to libkqueue returning a zero for the file descriptor. The assert happens
|
||||
// because something else has already closed FD zero by the time we get here, and Windows
|
||||
// doesn't like that very much. We only do this close when shutting down, so it should
|
||||
// be fine to just skip it.
|
||||
//
|
||||
// See https://github.com/mheily/libkqueue/issues/151 for more details.
|
||||
if ( event_queue != -1 )
|
||||
close(event_queue);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::InitPostScript()
|
||||
{
|
||||
wakeup = new WakeupHandler();
|
||||
poll_interval = BifConst::io_poll_interval_default;
|
||||
}
|
||||
void Manager::InitPostScript() {
|
||||
wakeup = new WakeupHandler();
|
||||
poll_interval = BifConst::io_poll_interval_default;
|
||||
}
|
||||
|
||||
void Manager::RemoveAll()
|
||||
{
|
||||
// We're cheating a bit here ...
|
||||
dont_counts = sources.size();
|
||||
}
|
||||
void Manager::RemoveAll() {
|
||||
// We're cheating a bit here ...
|
||||
dont_counts = sources.size();
|
||||
}
|
||||
|
||||
void Manager::Wakeup(std::string_view where)
|
||||
{
|
||||
if ( wakeup )
|
||||
wakeup->Ping(where);
|
||||
}
|
||||
void Manager::Wakeup(std::string_view where) {
|
||||
if ( wakeup )
|
||||
wakeup->Ping(where);
|
||||
}
|
||||
|
||||
void Manager::FindReadySources(ReadySources* ready)
|
||||
{
|
||||
ready->clear();
|
||||
void Manager::FindReadySources(ReadySources* ready) {
|
||||
ready->clear();
|
||||
|
||||
// Remove sources which have gone dry. For simplicity, we only
|
||||
// remove at most one each time.
|
||||
for ( SourceList::iterator i = sources.begin(); i != sources.end(); ++i )
|
||||
if ( ! (*i)->src->IsOpen() )
|
||||
{
|
||||
(*i)->src->Done();
|
||||
delete *i;
|
||||
sources.erase(i);
|
||||
break;
|
||||
}
|
||||
// Remove sources which have gone dry. For simplicity, we only
|
||||
// remove at most one each time.
|
||||
for ( SourceList::iterator i = sources.begin(); i != sources.end(); ++i )
|
||||
if ( ! (*i)->src->IsOpen() ) {
|
||||
(*i)->src->Done();
|
||||
delete *i;
|
||||
sources.erase(i);
|
||||
break;
|
||||
}
|
||||
|
||||
// If there aren't any sources and exit_only_after_terminate is false, just
|
||||
// return an empty set of sources. We want the main loop to end.
|
||||
if ( Size() == 0 && (! BifConst::exit_only_after_terminate || run_state::terminating) )
|
||||
return;
|
||||
// If there aren't any sources and exit_only_after_terminate is false, just
|
||||
// return an empty set of sources. We want the main loop to end.
|
||||
if ( Size() == 0 && (! BifConst::exit_only_after_terminate || run_state::terminating) )
|
||||
return;
|
||||
|
||||
double timeout = -1;
|
||||
IOSource* timeout_src = nullptr;
|
||||
bool time_to_poll = false;
|
||||
double timeout = -1;
|
||||
IOSource* timeout_src = nullptr;
|
||||
bool time_to_poll = false;
|
||||
|
||||
++poll_counter;
|
||||
if ( poll_counter % poll_interval == 0 )
|
||||
{
|
||||
poll_counter = 0;
|
||||
time_to_poll = true;
|
||||
}
|
||||
++poll_counter;
|
||||
if ( poll_counter % poll_interval == 0 ) {
|
||||
poll_counter = 0;
|
||||
time_to_poll = true;
|
||||
}
|
||||
|
||||
// Find the source with the next timeout value.
|
||||
for ( auto src : sources )
|
||||
{
|
||||
auto iosource = src->src;
|
||||
if ( iosource->IsOpen() )
|
||||
{
|
||||
double next = iosource->GetNextTimeout();
|
||||
// Find the source with the next timeout value.
|
||||
for ( auto src : sources ) {
|
||||
auto iosource = src->src;
|
||||
if ( iosource->IsOpen() ) {
|
||||
double next = iosource->GetNextTimeout();
|
||||
|
||||
if ( timeout == -1 || (next >= 0.0 && next < timeout) )
|
||||
{
|
||||
timeout = next;
|
||||
timeout_src = iosource;
|
||||
}
|
||||
if ( timeout == -1 || (next >= 0.0 && next < timeout) ) {
|
||||
timeout = next;
|
||||
timeout_src = iosource;
|
||||
}
|
||||
|
||||
// If a source has a zero timeout then it's ready. Just add it to the
|
||||
// list already. Only do this if it's not time to poll though, since
|
||||
// we don't want things in the vector passed into Poll() or it'll end
|
||||
// up inserting duplicates. A source with a zero timeout that was not
|
||||
// selected as the timeout_src can be safely added, whether it's time
|
||||
// to poll or not though.
|
||||
if ( next == 0 && (! time_to_poll || iosource != timeout_src) )
|
||||
{
|
||||
ready->push_back({iosource, -1, 0});
|
||||
}
|
||||
else if ( iosource == pkt_src )
|
||||
{
|
||||
if ( pkt_src->IsLive() )
|
||||
{
|
||||
if ( ! time_to_poll )
|
||||
// Avoid calling Poll() if we can help it since on very
|
||||
// high-traffic networks, we spend too much time in
|
||||
// Poll() and end up dropping packets.
|
||||
ready->push_back({pkt_src, -1, 0});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If a source has a zero timeout then it's ready. Just add it to the
|
||||
// list already. Only do this if it's not time to poll though, since
|
||||
// we don't want things in the vector passed into Poll() or it'll end
|
||||
// up inserting duplicates. A source with a zero timeout that was not
|
||||
// selected as the timeout_src can be safely added, whether it's time
|
||||
// to poll or not though.
|
||||
if ( next == 0 && (! time_to_poll || iosource != timeout_src) ) {
|
||||
ready->push_back({iosource, -1, 0});
|
||||
}
|
||||
else if ( iosource == pkt_src ) {
|
||||
if ( pkt_src->IsLive() ) {
|
||||
if ( ! time_to_poll )
|
||||
// Avoid calling Poll() if we can help it since on very
|
||||
// high-traffic networks, we spend too much time in
|
||||
// Poll() and end up dropping packets.
|
||||
ready->push_back({pkt_src, -1, 0});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DBG_LOG(DBG_MAINLOOP, "timeout: %f ready size: %zu time_to_poll: %d\n", timeout,
|
||||
ready->size(), time_to_poll);
|
||||
DBG_LOG(DBG_MAINLOOP, "timeout: %f ready size: %zu time_to_poll: %d\n", timeout, ready->size(), time_to_poll);
|
||||
|
||||
// If we didn't find any IOSources with zero timeouts or it's time to
|
||||
// force a poll, do that and return. Otherwise return the set of ready
|
||||
// sources that we have.
|
||||
if ( ready->empty() || time_to_poll )
|
||||
Poll(ready, timeout, timeout_src);
|
||||
}
|
||||
// If we didn't find any IOSources with zero timeouts or it's time to
|
||||
// force a poll, do that and return. Otherwise return the set of ready
|
||||
// sources that we have.
|
||||
if ( ready->empty() || time_to_poll )
|
||||
Poll(ready, timeout, timeout_src);
|
||||
}
|
||||
|
||||
void Manager::Poll(ReadySources* ready, double timeout, IOSource* timeout_src)
|
||||
{
|
||||
struct timespec kqueue_timeout;
|
||||
ConvertTimeout(timeout, kqueue_timeout);
|
||||
void Manager::Poll(ReadySources* ready, double timeout, IOSource* timeout_src) {
|
||||
struct timespec kqueue_timeout;
|
||||
ConvertTimeout(timeout, kqueue_timeout);
|
||||
|
||||
int ret = kevent(event_queue, NULL, 0, events.data(), events.size(), &kqueue_timeout);
|
||||
if ( ret == -1 )
|
||||
{
|
||||
// Ignore interrupts since we may catch one during shutdown and we don't want the
|
||||
// error to get printed.
|
||||
if ( errno != EINTR )
|
||||
reporter->InternalWarning("Error calling kevent: %s", strerror(errno));
|
||||
}
|
||||
else if ( ret == 0 )
|
||||
{
|
||||
// If a timeout_src was provided and nothing else was ready, we timed out
|
||||
// according to the given source's timeout and can add it as ready.
|
||||
if ( timeout_src )
|
||||
ready->push_back({timeout_src, -1, 0});
|
||||
}
|
||||
else
|
||||
{
|
||||
// kevent returns the number of events that are ready, so we only need to loop
|
||||
// over that many of them.
|
||||
bool timeout_src_added = false;
|
||||
for ( int i = 0; i < ret; i++ )
|
||||
{
|
||||
if ( events[i].filter == EVFILT_READ )
|
||||
{
|
||||
std::map<int, IOSource*>::const_iterator it = fd_map.find(events[i].ident);
|
||||
if ( it != fd_map.end() )
|
||||
ready->push_back({it->second, static_cast<int>(events[i].ident),
|
||||
IOSource::ProcessFlags::READ});
|
||||
}
|
||||
else if ( events[i].filter == EVFILT_WRITE )
|
||||
{
|
||||
std::map<int, IOSource*>::const_iterator it = write_fd_map.find(events[i].ident);
|
||||
if ( it != write_fd_map.end() )
|
||||
ready->push_back({it->second, static_cast<int>(events[i].ident),
|
||||
IOSource::ProcessFlags::WRITE});
|
||||
}
|
||||
int ret = kevent(event_queue, NULL, 0, events.data(), events.size(), &kqueue_timeout);
|
||||
if ( ret == -1 ) {
|
||||
// Ignore interrupts since we may catch one during shutdown and we don't want the
|
||||
// error to get printed.
|
||||
if ( errno != EINTR )
|
||||
reporter->InternalWarning("Error calling kevent: %s", strerror(errno));
|
||||
}
|
||||
else if ( ret == 0 ) {
|
||||
// If a timeout_src was provided and nothing else was ready, we timed out
|
||||
// according to the given source's timeout and can add it as ready.
|
||||
if ( timeout_src )
|
||||
ready->push_back({timeout_src, -1, 0});
|
||||
}
|
||||
else {
|
||||
// kevent returns the number of events that are ready, so we only need to loop
|
||||
// over that many of them.
|
||||
bool timeout_src_added = false;
|
||||
for ( int i = 0; i < ret; i++ ) {
|
||||
if ( events[i].filter == EVFILT_READ ) {
|
||||
std::map<int, IOSource*>::const_iterator it = fd_map.find(events[i].ident);
|
||||
if ( it != fd_map.end() )
|
||||
ready->push_back({it->second, static_cast<int>(events[i].ident), IOSource::ProcessFlags::READ});
|
||||
}
|
||||
else if ( events[i].filter == EVFILT_WRITE ) {
|
||||
std::map<int, IOSource*>::const_iterator it = write_fd_map.find(events[i].ident);
|
||||
if ( it != write_fd_map.end() )
|
||||
ready->push_back({it->second, static_cast<int>(events[i].ident), IOSource::ProcessFlags::WRITE});
|
||||
}
|
||||
|
||||
// If we added a source that is the same as the passed timeout_src, take
|
||||
// note as to avoid adding it twice.
|
||||
timeout_src_added |= ready->size() > 0 ? ready->back().src == timeout_src : false;
|
||||
}
|
||||
// If we added a source that is the same as the passed timeout_src, take
|
||||
// note as to avoid adding it twice.
|
||||
timeout_src_added |= ready->size() > 0 ? ready->back().src == timeout_src : false;
|
||||
}
|
||||
|
||||
// A timeout_src with a zero timeout can be considered ready.
|
||||
if ( timeout_src && timeout == 0.0 && ! timeout_src_added )
|
||||
ready->push_back({timeout_src, -1, 0});
|
||||
}
|
||||
}
|
||||
// A timeout_src with a zero timeout can be considered ready.
|
||||
if ( timeout_src && timeout == 0.0 && ! timeout_src_added )
|
||||
ready->push_back({timeout_src, -1, 0});
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::ConvertTimeout(double timeout, struct timespec& spec)
|
||||
{
|
||||
// If timeout ended up -1, set it to some nominal value just to keep the loop
|
||||
// from blocking forever. This is the case of exit_only_after_terminate when
|
||||
// there isn't anything else going on.
|
||||
if ( timeout < 0 )
|
||||
{
|
||||
spec.tv_sec = 0;
|
||||
spec.tv_nsec = 1e8;
|
||||
}
|
||||
else
|
||||
{
|
||||
spec.tv_sec = static_cast<time_t>(timeout);
|
||||
spec.tv_nsec = static_cast<long>((timeout - spec.tv_sec) * 1e9);
|
||||
}
|
||||
}
|
||||
void Manager::ConvertTimeout(double timeout, struct timespec& spec) {
|
||||
// If timeout ended up -1, set it to some nominal value just to keep the loop
|
||||
// from blocking forever. This is the case of exit_only_after_terminate when
|
||||
// there isn't anything else going on.
|
||||
if ( timeout < 0 ) {
|
||||
spec.tv_sec = 0;
|
||||
spec.tv_nsec = 1e8;
|
||||
}
|
||||
else {
|
||||
spec.tv_sec = static_cast<time_t>(timeout);
|
||||
spec.tv_nsec = static_cast<long>((timeout - spec.tv_sec) * 1e9);
|
||||
}
|
||||
}
|
||||
|
||||
bool Manager::RegisterFd(int fd, IOSource* src, int flags)
|
||||
{
|
||||
std::vector<struct kevent> new_events;
|
||||
bool Manager::RegisterFd(int fd, IOSource* src, int flags) {
|
||||
std::vector<struct kevent> new_events;
|
||||
|
||||
if ( (flags & IOSource::READ) != 0 )
|
||||
{
|
||||
if ( fd_map.count(fd) == 0 )
|
||||
{
|
||||
new_events.push_back({});
|
||||
EV_SET(&(new_events.back()), fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
|
||||
}
|
||||
}
|
||||
if ( (flags & IOSource::WRITE) != 0 )
|
||||
{
|
||||
if ( write_fd_map.count(fd) == 0 )
|
||||
{
|
||||
new_events.push_back({});
|
||||
EV_SET(&(new_events.back()), fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
|
||||
}
|
||||
}
|
||||
if ( (flags & IOSource::READ) != 0 ) {
|
||||
if ( fd_map.count(fd) == 0 ) {
|
||||
new_events.push_back({});
|
||||
EV_SET(&(new_events.back()), fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
|
||||
}
|
||||
}
|
||||
if ( (flags & IOSource::WRITE) != 0 ) {
|
||||
if ( write_fd_map.count(fd) == 0 ) {
|
||||
new_events.push_back({});
|
||||
EV_SET(&(new_events.back()), fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! new_events.empty() )
|
||||
{
|
||||
int ret = kevent(event_queue, new_events.data(), new_events.size(), NULL, 0, NULL);
|
||||
if ( ret != -1 )
|
||||
{
|
||||
DBG_LOG(DBG_MAINLOOP, "Registered fd %d from %s", fd, src->Tag());
|
||||
for ( const auto& a : new_events )
|
||||
events.push_back({});
|
||||
if ( ! new_events.empty() ) {
|
||||
int ret = kevent(event_queue, new_events.data(), new_events.size(), NULL, 0, NULL);
|
||||
if ( ret != -1 ) {
|
||||
DBG_LOG(DBG_MAINLOOP, "Registered fd %d from %s", fd, src->Tag());
|
||||
for ( const auto& a : new_events )
|
||||
events.push_back({});
|
||||
|
||||
if ( (flags & IOSource::READ) != 0 )
|
||||
fd_map[fd] = src;
|
||||
if ( (flags & IOSource::WRITE) != 0 )
|
||||
write_fd_map[fd] = src;
|
||||
if ( (flags & IOSource::READ) != 0 )
|
||||
fd_map[fd] = src;
|
||||
if ( (flags & IOSource::WRITE) != 0 )
|
||||
write_fd_map[fd] = src;
|
||||
|
||||
Wakeup("RegisterFd");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
reporter->Error("Failed to register fd %d from %s: %s (flags %d)", fd, src->Tag(),
|
||||
strerror(errno), flags);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Wakeup("RegisterFd");
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
reporter->Error("Failed to register fd %d from %s: %s (flags %d)", fd, src->Tag(), strerror(errno), flags);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Manager::UnregisterFd(int fd, IOSource* src, int flags)
|
||||
{
|
||||
std::vector<struct kevent> new_events;
|
||||
bool Manager::UnregisterFd(int fd, IOSource* src, int flags) {
|
||||
std::vector<struct kevent> new_events;
|
||||
|
||||
if ( (flags & IOSource::READ) != 0 )
|
||||
{
|
||||
if ( fd_map.count(fd) != 0 )
|
||||
{
|
||||
new_events.push_back({});
|
||||
EV_SET(&(new_events.back()), fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
|
||||
}
|
||||
}
|
||||
if ( (flags & IOSource::WRITE) != 0 )
|
||||
{
|
||||
if ( write_fd_map.count(fd) != 0 )
|
||||
{
|
||||
new_events.push_back({});
|
||||
EV_SET(&(new_events.back()), fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
|
||||
}
|
||||
}
|
||||
if ( (flags & IOSource::READ) != 0 ) {
|
||||
if ( fd_map.count(fd) != 0 ) {
|
||||
new_events.push_back({});
|
||||
EV_SET(&(new_events.back()), fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
|
||||
}
|
||||
}
|
||||
if ( (flags & IOSource::WRITE) != 0 ) {
|
||||
if ( write_fd_map.count(fd) != 0 ) {
|
||||
new_events.push_back({});
|
||||
EV_SET(&(new_events.back()), fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! new_events.empty() )
|
||||
{
|
||||
int ret = kevent(event_queue, new_events.data(), new_events.size(), NULL, 0, NULL);
|
||||
if ( ret != -1 )
|
||||
{
|
||||
DBG_LOG(DBG_MAINLOOP, "Unregistered fd %d from %s", fd, src->Tag());
|
||||
for ( const auto& a : new_events )
|
||||
events.pop_back();
|
||||
if ( ! new_events.empty() ) {
|
||||
int ret = kevent(event_queue, new_events.data(), new_events.size(), NULL, 0, NULL);
|
||||
if ( ret != -1 ) {
|
||||
DBG_LOG(DBG_MAINLOOP, "Unregistered fd %d from %s", fd, src->Tag());
|
||||
for ( const auto& a : new_events )
|
||||
events.pop_back();
|
||||
|
||||
if ( (flags & IOSource::READ) != 0 )
|
||||
fd_map.erase(fd);
|
||||
if ( (flags & IOSource::WRITE) != 0 )
|
||||
write_fd_map.erase(fd);
|
||||
if ( (flags & IOSource::READ) != 0 )
|
||||
fd_map.erase(fd);
|
||||
if ( (flags & IOSource::WRITE) != 0 )
|
||||
write_fd_map.erase(fd);
|
||||
|
||||
Wakeup("UnregisterFd");
|
||||
return true;
|
||||
}
|
||||
Wakeup("UnregisterFd");
|
||||
return true;
|
||||
}
|
||||
|
||||
// We don't care about failure here. If it failed to unregister, it's likely because
|
||||
// the file descriptor was already closed, and kqueue already automatically removed
|
||||
// it.
|
||||
}
|
||||
else
|
||||
{
|
||||
reporter->Error("Attempted to unregister an unknown file descriptor %d from %s", fd,
|
||||
src->Tag());
|
||||
return false;
|
||||
}
|
||||
// We don't care about failure here. If it failed to unregister, it's likely because
|
||||
// the file descriptor was already closed, and kqueue already automatically removed
|
||||
// it.
|
||||
}
|
||||
else {
|
||||
reporter->Error("Attempted to unregister an unknown file descriptor %d from %s", fd, src->Tag());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Manager::Register(IOSource* src, bool dont_count, bool manage_lifetime)
|
||||
{
|
||||
// First see if we already have registered that source. If so, just
|
||||
// adjust dont_count.
|
||||
for ( const auto& iosrc : sources )
|
||||
{
|
||||
if ( iosrc->src == src )
|
||||
{
|
||||
if ( iosrc->dont_count != dont_count )
|
||||
// Adjust the global counter.
|
||||
dont_counts += (dont_count ? 1 : -1);
|
||||
void Manager::Register(IOSource* src, bool dont_count, bool manage_lifetime) {
|
||||
// First see if we already have registered that source. If so, just
|
||||
// adjust dont_count.
|
||||
for ( const auto& iosrc : sources ) {
|
||||
if ( iosrc->src == src ) {
|
||||
if ( iosrc->dont_count != dont_count )
|
||||
// Adjust the global counter.
|
||||
dont_counts += (dont_count ? 1 : -1);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
src->InitSource();
|
||||
Source* s = new Source;
|
||||
s->src = src;
|
||||
s->dont_count = dont_count;
|
||||
s->manage_lifetime = manage_lifetime;
|
||||
if ( dont_count )
|
||||
++dont_counts;
|
||||
src->InitSource();
|
||||
Source* s = new Source;
|
||||
s->src = src;
|
||||
s->dont_count = dont_count;
|
||||
s->manage_lifetime = manage_lifetime;
|
||||
if ( dont_count )
|
||||
++dont_counts;
|
||||
|
||||
sources.push_back(s);
|
||||
}
|
||||
sources.push_back(s);
|
||||
}
|
||||
|
||||
void Manager::Register(PktSrc* src)
|
||||
{
|
||||
pkt_src = src;
|
||||
void Manager::Register(PktSrc* src) {
|
||||
pkt_src = src;
|
||||
|
||||
Register(src, false);
|
||||
Register(src, false);
|
||||
|
||||
// Once we know if the source is live or not, adapt the
|
||||
// poll_interval accordingly.
|
||||
//
|
||||
// Note that src->IsLive() is only valid after calling Register().
|
||||
if ( src->IsLive() )
|
||||
poll_interval = BifConst::io_poll_interval_live;
|
||||
else if ( run_state::pseudo_realtime )
|
||||
poll_interval = 1;
|
||||
}
|
||||
// Once we know if the source is live or not, adapt the
|
||||
// poll_interval accordingly.
|
||||
//
|
||||
// Note that src->IsLive() is only valid after calling Register().
|
||||
if ( src->IsLive() )
|
||||
poll_interval = BifConst::io_poll_interval_live;
|
||||
else if ( run_state::pseudo_realtime )
|
||||
poll_interval = 1;
|
||||
}
|
||||
|
||||
static std::pair<std::string, std::string> split_prefix(std::string path)
|
||||
{
|
||||
// See if the path comes with a prefix telling us which type of
|
||||
// PktSrc to use. If not, choose default.
|
||||
std::string prefix;
|
||||
static std::pair<std::string, std::string> split_prefix(std::string path) {
|
||||
// See if the path comes with a prefix telling us which type of
|
||||
// PktSrc to use. If not, choose default.
|
||||
std::string prefix;
|
||||
|
||||
std::string::size_type i = path.find("::");
|
||||
if ( i != std::string::npos )
|
||||
{
|
||||
prefix = path.substr(0, i);
|
||||
path = path.substr(i + 2, std::string::npos);
|
||||
}
|
||||
std::string::size_type i = path.find("::");
|
||||
if ( i != std::string::npos ) {
|
||||
prefix = path.substr(0, i);
|
||||
path = path.substr(i + 2, std::string::npos);
|
||||
}
|
||||
|
||||
else
|
||||
prefix = DEFAULT_PREFIX;
|
||||
else
|
||||
prefix = DEFAULT_PREFIX;
|
||||
|
||||
return std::make_pair(prefix, path);
|
||||
}
|
||||
return std::make_pair(prefix, path);
|
||||
}
|
||||
|
||||
PktSrc* Manager::OpenPktSrc(const std::string& path, bool is_live)
|
||||
{
|
||||
std::pair<std::string, std::string> t = split_prefix(path);
|
||||
const auto& prefix = t.first;
|
||||
const auto& npath = t.second;
|
||||
PktSrc* Manager::OpenPktSrc(const std::string& path, bool is_live) {
|
||||
std::pair<std::string, std::string> t = split_prefix(path);
|
||||
const auto& prefix = t.first;
|
||||
const auto& npath = t.second;
|
||||
|
||||
// Find the component providing packet sources of the requested prefix.
|
||||
// Find the component providing packet sources of the requested prefix.
|
||||
|
||||
PktSrcComponent* component = nullptr;
|
||||
PktSrcComponent* component = nullptr;
|
||||
|
||||
std::list<PktSrcComponent*> all_components = plugin_mgr->Components<PktSrcComponent>();
|
||||
for ( const auto& c : all_components )
|
||||
{
|
||||
if ( c->HandlesPrefix(prefix) &&
|
||||
((is_live && c->DoesLive()) || (! is_live && c->DoesTrace())) )
|
||||
{
|
||||
component = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::list<PktSrcComponent*> all_components = plugin_mgr->Components<PktSrcComponent>();
|
||||
for ( const auto& c : all_components ) {
|
||||
if ( c->HandlesPrefix(prefix) && ((is_live && c->DoesLive()) || (! is_live && c->DoesTrace())) ) {
|
||||
component = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! component )
|
||||
reporter->FatalError("type of packet source '%s' not recognized, or mode not supported",
|
||||
prefix.c_str());
|
||||
if ( ! component )
|
||||
reporter->FatalError("type of packet source '%s' not recognized, or mode not supported", prefix.c_str());
|
||||
|
||||
// Instantiate packet source.
|
||||
// Instantiate packet source.
|
||||
|
||||
PktSrc* ps = (*component->Factory())(npath, is_live);
|
||||
assert(ps);
|
||||
PktSrc* ps = (*component->Factory())(npath, is_live);
|
||||
assert(ps);
|
||||
|
||||
DBG_LOG(DBG_PKTIO, "Created packet source of type %s for %s", component->Name().c_str(),
|
||||
npath.c_str());
|
||||
DBG_LOG(DBG_PKTIO, "Created packet source of type %s for %s", component->Name().c_str(), npath.c_str());
|
||||
|
||||
Register(ps);
|
||||
return ps;
|
||||
}
|
||||
Register(ps);
|
||||
return ps;
|
||||
}
|
||||
|
||||
PktDumper* Manager::OpenPktDumper(const std::string& path, bool append)
|
||||
{
|
||||
std::pair<std::string, std::string> t = split_prefix(path);
|
||||
std::string prefix = t.first;
|
||||
std::string npath = t.second;
|
||||
PktDumper* Manager::OpenPktDumper(const std::string& path, bool append) {
|
||||
std::pair<std::string, std::string> t = split_prefix(path);
|
||||
std::string prefix = t.first;
|
||||
std::string npath = t.second;
|
||||
|
||||
// Find the component providing packet dumpers of the requested prefix.
|
||||
// Find the component providing packet dumpers of the requested prefix.
|
||||
|
||||
PktDumperComponent* component = nullptr;
|
||||
PktDumperComponent* component = nullptr;
|
||||
|
||||
std::list<PktDumperComponent*> all_components = plugin_mgr->Components<PktDumperComponent>();
|
||||
for ( const auto& c : all_components )
|
||||
{
|
||||
if ( c->HandlesPrefix(prefix) )
|
||||
{
|
||||
component = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::list<PktDumperComponent*> all_components = plugin_mgr->Components<PktDumperComponent>();
|
||||
for ( const auto& c : all_components ) {
|
||||
if ( c->HandlesPrefix(prefix) ) {
|
||||
component = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! component )
|
||||
reporter->FatalError("type of packet dumper '%s' not recognized", prefix.c_str());
|
||||
if ( ! component )
|
||||
reporter->FatalError("type of packet dumper '%s' not recognized", prefix.c_str());
|
||||
|
||||
// Instantiate packet dumper.
|
||||
// Instantiate packet dumper.
|
||||
|
||||
PktDumper* pd = (*component->Factory())(npath, append);
|
||||
assert(pd);
|
||||
PktDumper* pd = (*component->Factory())(npath, append);
|
||||
assert(pd);
|
||||
|
||||
if ( ! pd->IsOpen() && pd->IsError() )
|
||||
// Set an error message if it didn't open successfully.
|
||||
pd->Error("could not open");
|
||||
if ( ! pd->IsOpen() && pd->IsError() )
|
||||
// Set an error message if it didn't open successfully.
|
||||
pd->Error("could not open");
|
||||
|
||||
DBG_LOG(DBG_PKTIO, "Created packer dumper of type %s for %s", component->Name().c_str(),
|
||||
npath.c_str());
|
||||
DBG_LOG(DBG_PKTIO, "Created packer dumper of type %s for %s", component->Name().c_str(), npath.c_str());
|
||||
|
||||
pd->Init();
|
||||
pkt_dumpers.push_back(pd);
|
||||
pd->Init();
|
||||
pkt_dumpers.push_back(pd);
|
||||
|
||||
return pd;
|
||||
}
|
||||
return pd;
|
||||
}
|
||||
|
||||
} // namespace zeek::iosource
|
||||
} // namespace zeek::iosource
|
||||
|
|
|
@ -14,10 +14,8 @@
|
|||
struct timespec;
|
||||
struct kevent;
|
||||
|
||||
namespace zeek
|
||||
{
|
||||
namespace iosource
|
||||
{
|
||||
namespace zeek {
|
||||
namespace iosource {
|
||||
|
||||
class PktSrc;
|
||||
class PktDumper;
|
||||
|
@ -26,209 +24,205 @@ class PktDumper;
|
|||
* Manager class for IO sources. This handles all of the polling of sources
|
||||
* in the main loop.
|
||||
*/
|
||||
class Manager
|
||||
{
|
||||
class Manager {
|
||||
public:
|
||||
struct ReadySource
|
||||
{
|
||||
IOSource* src = nullptr;
|
||||
int fd = -1;
|
||||
int flags = 0;
|
||||
};
|
||||
struct ReadySource {
|
||||
IOSource* src = nullptr;
|
||||
int fd = -1;
|
||||
int flags = 0;
|
||||
};
|
||||
|
||||
using ReadySources = std::vector<ReadySource>;
|
||||
using ReadySources = std::vector<ReadySource>;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
Manager();
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
Manager();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~Manager();
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~Manager();
|
||||
|
||||
/**
|
||||
* Initializes some extra fields that can't be done during the
|
||||
* due to dependencies on other objects being initialized first.
|
||||
*/
|
||||
void InitPostScript();
|
||||
/**
|
||||
* Initializes some extra fields that can't be done during the
|
||||
* due to dependencies on other objects being initialized first.
|
||||
*/
|
||||
void InitPostScript();
|
||||
|
||||
/**
|
||||
* Registers an IOSource with the manager. If the source is already
|
||||
* registered, the method will update its *dont_count* value but not
|
||||
* do anything else.
|
||||
*
|
||||
* @param src The source. The manager takes ownership.
|
||||
*
|
||||
* @param dont_count If true, this source does not contribute to the
|
||||
* number of IOSources returned by Size(). The effect is that if all
|
||||
* sources except for the non-counting ones have gone dry, processing
|
||||
* will shut down.
|
||||
*/
|
||||
void Register(IOSource* src, bool dont_count = false, bool manage_lifetime = true);
|
||||
/**
|
||||
* Registers an IOSource with the manager. If the source is already
|
||||
* registered, the method will update its *dont_count* value but not
|
||||
* do anything else.
|
||||
*
|
||||
* @param src The source. The manager takes ownership.
|
||||
*
|
||||
* @param dont_count If true, this source does not contribute to the
|
||||
* number of IOSources returned by Size(). The effect is that if all
|
||||
* sources except for the non-counting ones have gone dry, processing
|
||||
* will shut down.
|
||||
*/
|
||||
void Register(IOSource* src, bool dont_count = false, bool manage_lifetime = true);
|
||||
|
||||
/**
|
||||
* Returns the number of registered and still active sources,
|
||||
* excluding those that are registered as \a dont_count.
|
||||
*/
|
||||
int Size() const { return sources.size() - dont_counts; }
|
||||
/**
|
||||
* Returns the number of registered and still active sources,
|
||||
* excluding those that are registered as \a dont_count.
|
||||
*/
|
||||
int Size() const { return sources.size() - dont_counts; }
|
||||
|
||||
/**
|
||||
* Returns total number of sources including dont_counts;
|
||||
*/
|
||||
int TotalSize() const { return sources.size(); }
|
||||
/**
|
||||
* Returns total number of sources including dont_counts;
|
||||
*/
|
||||
int TotalSize() const { return sources.size(); }
|
||||
|
||||
/**
|
||||
* Returns the registered PktSrc. If not source is registered yet,
|
||||
* returns a nullptr.
|
||||
*/
|
||||
PktSrc* GetPktSrc() const { return pkt_src; }
|
||||
/**
|
||||
* Returns the registered PktSrc. If not source is registered yet,
|
||||
* returns a nullptr.
|
||||
*/
|
||||
PktSrc* GetPktSrc() const { return pkt_src; }
|
||||
|
||||
/**
|
||||
* Terminate all processing immediately by removing all sources (and
|
||||
* therefore now returning a Size() of zero).
|
||||
*/
|
||||
void Terminate() { RemoveAll(); }
|
||||
/**
|
||||
* Terminate all processing immediately by removing all sources (and
|
||||
* therefore now returning a Size() of zero).
|
||||
*/
|
||||
void Terminate() { RemoveAll(); }
|
||||
|
||||
/**
|
||||
* Opens a new packet source.
|
||||
*
|
||||
* @param path The interface or file name, as one would give to zeek \c -i.
|
||||
*
|
||||
* @param is_live True if \a path represents a live interface, false
|
||||
* for a file.
|
||||
*
|
||||
* @return The new packet source, or null if an error occurred.
|
||||
*/
|
||||
PktSrc* OpenPktSrc(const std::string& path, bool is_live);
|
||||
/**
|
||||
* Opens a new packet source.
|
||||
*
|
||||
* @param path The interface or file name, as one would give to zeek \c -i.
|
||||
*
|
||||
* @param is_live True if \a path represents a live interface, false
|
||||
* for a file.
|
||||
*
|
||||
* @return The new packet source, or null if an error occurred.
|
||||
*/
|
||||
PktSrc* OpenPktSrc(const std::string& path, bool is_live);
|
||||
|
||||
/**
|
||||
* Opens a new packet dumper.
|
||||
*
|
||||
* @param path The file name to dump into.
|
||||
*
|
||||
* @param append True to append if \a path already exists.
|
||||
*
|
||||
* @return The new packet dumper, or null if an error occurred.
|
||||
*/
|
||||
PktDumper* OpenPktDumper(const std::string& path, bool append);
|
||||
/**
|
||||
* Opens a new packet dumper.
|
||||
*
|
||||
* @param path The file name to dump into.
|
||||
*
|
||||
* @param append True to append if \a path already exists.
|
||||
*
|
||||
* @return The new packet dumper, or null if an error occurred.
|
||||
*/
|
||||
PktDumper* OpenPktDumper(const std::string& path, bool append);
|
||||
|
||||
/**
|
||||
* Finds the sources that have data ready to be processed.
|
||||
*
|
||||
* @param ready A vector used to return the set of sources that are ready.
|
||||
*/
|
||||
void FindReadySources(ReadySources* ready);
|
||||
/**
|
||||
* Finds the sources that have data ready to be processed.
|
||||
*
|
||||
* @param ready A vector used to return the set of sources that are ready.
|
||||
*/
|
||||
void FindReadySources(ReadySources* ready);
|
||||
|
||||
/**
|
||||
* Registers a file descriptor and associated IOSource with the manager
|
||||
* to be checked during FindReadySources. This will register the file
|
||||
* descriptor to check for read events.
|
||||
*
|
||||
* @param fd A file descriptor pointing at some resource that should be
|
||||
* checked for readiness.
|
||||
* @param src The IOSource that owns the file descriptor.
|
||||
* @param flags A combination of values from IOSource::ProcessFlags for
|
||||
* which modes we should register for this file descriptor.
|
||||
*/
|
||||
bool RegisterFd(int fd, IOSource* src, int flags = IOSource::READ);
|
||||
/**
|
||||
* Registers a file descriptor and associated IOSource with the manager
|
||||
* to be checked during FindReadySources. This will register the file
|
||||
* descriptor to check for read events.
|
||||
*
|
||||
* @param fd A file descriptor pointing at some resource that should be
|
||||
* checked for readiness.
|
||||
* @param src The IOSource that owns the file descriptor.
|
||||
* @param flags A combination of values from IOSource::ProcessFlags for
|
||||
* which modes we should register for this file descriptor.
|
||||
*/
|
||||
bool RegisterFd(int fd, IOSource* src, int flags = IOSource::READ);
|
||||
|
||||
/**
|
||||
* Unregisters a file descriptor from the FindReadySources checks.
|
||||
*/
|
||||
bool UnregisterFd(int fd, IOSource* src, int flags = IOSource::READ);
|
||||
/**
|
||||
* Unregisters a file descriptor from the FindReadySources checks.
|
||||
*/
|
||||
bool UnregisterFd(int fd, IOSource* src, int flags = IOSource::READ);
|
||||
|
||||
/**
|
||||
* Forces the poll in FindReadySources to wake up immediately. This method
|
||||
* is called during RegisterFd and UnregisterFd since those methods cause
|
||||
* changes to the active set of file descriptors.
|
||||
*/
|
||||
void Wakeup(std::string_view where);
|
||||
/**
|
||||
* Forces the poll in FindReadySources to wake up immediately. This method
|
||||
* is called during RegisterFd and UnregisterFd since those methods cause
|
||||
* changes to the active set of file descriptors.
|
||||
*/
|
||||
void Wakeup(std::string_view where);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Calls the appropriate poll method to gather a set of IOSources that are
|
||||
* ready for processing.
|
||||
*
|
||||
* @param ready a vector used to return the ready sources.
|
||||
* @param timeout the value to be used for the timeout of the poll. This
|
||||
* should be a value relative to the current network time, not an
|
||||
* absolute time value. This may be zero to cause an infinite timeout or
|
||||
* -1 to force a very short timeout.
|
||||
* @param timeout_src The source associated with the current timeout value.
|
||||
* This is typically a timer manager object.
|
||||
*/
|
||||
void Poll(ReadySources* ready, double timeout, IOSource* timeout_src);
|
||||
/**
|
||||
* Calls the appropriate poll method to gather a set of IOSources that are
|
||||
* ready for processing.
|
||||
*
|
||||
* @param ready a vector used to return the ready sources.
|
||||
* @param timeout the value to be used for the timeout of the poll. This
|
||||
* should be a value relative to the current network time, not an
|
||||
* absolute time value. This may be zero to cause an infinite timeout or
|
||||
* -1 to force a very short timeout.
|
||||
* @param timeout_src The source associated with the current timeout value.
|
||||
* This is typically a timer manager object.
|
||||
*/
|
||||
void Poll(ReadySources* ready, double timeout, IOSource* timeout_src);
|
||||
|
||||
/**
|
||||
* Converts a double timeout value into a timespec struct used for calls
|
||||
* to kevent().
|
||||
*/
|
||||
void ConvertTimeout(double timeout, struct timespec& spec);
|
||||
/**
|
||||
* Converts a double timeout value into a timespec struct used for calls
|
||||
* to kevent().
|
||||
*/
|
||||
void ConvertTimeout(double timeout, struct timespec& spec);
|
||||
|
||||
/**
|
||||
* Specialized registration method for packet sources.
|
||||
*/
|
||||
void Register(PktSrc* src);
|
||||
/**
|
||||
* Specialized registration method for packet sources.
|
||||
*/
|
||||
void Register(PktSrc* src);
|
||||
|
||||
void RemoveAll();
|
||||
void RemoveAll();
|
||||
|
||||
class WakeupHandler final : public IOSource
|
||||
{
|
||||
public:
|
||||
WakeupHandler();
|
||||
~WakeupHandler();
|
||||
class WakeupHandler final : public IOSource {
|
||||
public:
|
||||
WakeupHandler();
|
||||
~WakeupHandler();
|
||||
|
||||
/**
|
||||
* Tells the handler to wake up the loop by firing the flare.
|
||||
*
|
||||
* @param where a string denoting where this ping was called from. Used
|
||||
* for debugging output.
|
||||
*/
|
||||
void Ping(std::string_view where);
|
||||
/**
|
||||
* Tells the handler to wake up the loop by firing the flare.
|
||||
*
|
||||
* @param where a string denoting where this ping was called from. Used
|
||||
* for debugging output.
|
||||
*/
|
||||
void Ping(std::string_view where);
|
||||
|
||||
// IOSource API methods
|
||||
void Process() override;
|
||||
const char* Tag() override { return "WakeupHandler"; }
|
||||
double GetNextTimeout() override { return -1; }
|
||||
// IOSource API methods
|
||||
void Process() override;
|
||||
const char* Tag() override { return "WakeupHandler"; }
|
||||
double GetNextTimeout() override { return -1; }
|
||||
|
||||
private:
|
||||
zeek::detail::Flare flare;
|
||||
};
|
||||
private:
|
||||
zeek::detail::Flare flare;
|
||||
};
|
||||
|
||||
struct Source
|
||||
{
|
||||
IOSource* src = nullptr;
|
||||
bool dont_count = false;
|
||||
bool manage_lifetime = false;
|
||||
};
|
||||
struct Source {
|
||||
IOSource* src = nullptr;
|
||||
bool dont_count = false;
|
||||
bool manage_lifetime = false;
|
||||
};
|
||||
|
||||
using SourceList = std::vector<Source*>;
|
||||
SourceList sources;
|
||||
using SourceList = std::vector<Source*>;
|
||||
SourceList sources;
|
||||
|
||||
using PktDumperList = std::vector<PktDumper*>;
|
||||
PktDumperList pkt_dumpers;
|
||||
using PktDumperList = std::vector<PktDumper*>;
|
||||
PktDumperList pkt_dumpers;
|
||||
|
||||
PktSrc* pkt_src = nullptr;
|
||||
PktSrc* pkt_src = nullptr;
|
||||
|
||||
int dont_counts = 0;
|
||||
int zero_timeout_count = 0;
|
||||
WakeupHandler* wakeup = nullptr;
|
||||
int poll_counter = 0;
|
||||
int poll_interval = 0; // Set in InitPostScript() based on const value.
|
||||
int dont_counts = 0;
|
||||
int zero_timeout_count = 0;
|
||||
WakeupHandler* wakeup = nullptr;
|
||||
int poll_counter = 0;
|
||||
int poll_interval = 0; // Set in InitPostScript() based on const value.
|
||||
|
||||
int event_queue = -1;
|
||||
std::map<int, IOSource*> fd_map;
|
||||
std::map<int, IOSource*> write_fd_map;
|
||||
int event_queue = -1;
|
||||
std::map<int, IOSource*> fd_map;
|
||||
std::map<int, IOSource*> write_fd_map;
|
||||
|
||||
// This is only used for the output of the call to kqueue in FindReadySources().
|
||||
// The actual events are stored as part of the queue.
|
||||
std::vector<struct kevent> events;
|
||||
};
|
||||
// This is only used for the output of the call to kqueue in FindReadySources().
|
||||
// The actual events are stored as part of the queue.
|
||||
std::vector<struct kevent> events;
|
||||
};
|
||||
|
||||
} // namespace iosource
|
||||
} // namespace iosource
|
||||
|
||||
extern iosource::Manager* iosource_mgr;
|
||||
|
||||
} // namespace zeek
|
||||
} // namespace zeek
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "zeek/iosource/Packet.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
extern "C" {
|
||||
#include <pcap.h>
|
||||
#ifdef HAVE_NET_ETHERNET_H
|
||||
#include <net/ethernet.h>
|
||||
|
@ -13,7 +12,7 @@ extern "C"
|
|||
#elif defined(HAVE_NET_ETHERTYPES_H)
|
||||
#include <net/ethertypes.h>
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#include "zeek/Desc.h"
|
||||
#include "zeek/IP.h"
|
||||
|
@ -22,177 +21,166 @@ extern "C"
|
|||
#include "zeek/iosource/Manager.h"
|
||||
#include "zeek/packet_analysis/Manager.h"
|
||||
|
||||
namespace zeek
|
||||
{
|
||||
namespace zeek {
|
||||
|
||||
void Packet::Init(int arg_link_type, pkt_timeval* arg_ts, uint32_t arg_caplen, uint32_t arg_len,
|
||||
const u_char* arg_data, bool arg_copy, std::string arg_tag)
|
||||
{
|
||||
if ( data && copy )
|
||||
delete[] data;
|
||||
void Packet::Init(int arg_link_type, pkt_timeval* arg_ts, uint32_t arg_caplen, uint32_t arg_len, const u_char* arg_data,
|
||||
bool arg_copy, std::string arg_tag) {
|
||||
if ( data && copy )
|
||||
delete[] data;
|
||||
|
||||
link_type = arg_link_type;
|
||||
ts = *arg_ts;
|
||||
cap_len = arg_caplen;
|
||||
len = arg_len;
|
||||
tag = std::move(arg_tag);
|
||||
link_type = arg_link_type;
|
||||
ts = *arg_ts;
|
||||
cap_len = arg_caplen;
|
||||
len = arg_len;
|
||||
tag = std::move(arg_tag);
|
||||
|
||||
copy = arg_copy;
|
||||
copy = arg_copy;
|
||||
|
||||
if ( arg_data && arg_copy )
|
||||
{
|
||||
data = new u_char[arg_caplen];
|
||||
memcpy(const_cast<u_char*>(data), arg_data, arg_caplen);
|
||||
}
|
||||
else
|
||||
data = arg_data;
|
||||
if ( arg_data && arg_copy ) {
|
||||
data = new u_char[arg_caplen];
|
||||
memcpy(const_cast<u_char*>(data), arg_data, arg_caplen);
|
||||
}
|
||||
else
|
||||
data = arg_data;
|
||||
|
||||
dump_packet = false;
|
||||
dump_packet = false;
|
||||
|
||||
time = ts.tv_sec + double(ts.tv_usec) / 1e6;
|
||||
eth_type = 0;
|
||||
vlan = 0;
|
||||
inner_vlan = 0;
|
||||
time = ts.tv_sec + double(ts.tv_usec) / 1e6;
|
||||
eth_type = 0;
|
||||
vlan = 0;
|
||||
inner_vlan = 0;
|
||||
|
||||
l2_src = nullptr;
|
||||
l2_dst = nullptr;
|
||||
l2_checksummed = false;
|
||||
l2_src = nullptr;
|
||||
l2_dst = nullptr;
|
||||
l2_checksummed = false;
|
||||
|
||||
l3_proto = L3_UNKNOWN;
|
||||
l3_checksummed = false;
|
||||
l3_proto = L3_UNKNOWN;
|
||||
l3_checksummed = false;
|
||||
|
||||
l4_checksummed = false;
|
||||
l4_checksummed = false;
|
||||
|
||||
encap.reset();
|
||||
ip_hdr.reset();
|
||||
encap.reset();
|
||||
ip_hdr.reset();
|
||||
|
||||
proto = -1;
|
||||
tunnel_type = BifEnum::Tunnel::NONE;
|
||||
gre_version = -1;
|
||||
gre_link_type = DLT_RAW;
|
||||
proto = -1;
|
||||
tunnel_type = BifEnum::Tunnel::NONE;
|
||||
gre_version = -1;
|
||||
gre_link_type = DLT_RAW;
|
||||
|
||||
processed = false;
|
||||
}
|
||||
processed = false;
|
||||
}
|
||||
|
||||
Packet::~Packet()
|
||||
{
|
||||
if ( copy )
|
||||
delete[] data;
|
||||
}
|
||||
Packet::~Packet() {
|
||||
if ( copy )
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
RecordValPtr Packet::ToRawPktHdrVal() const
|
||||
{
|
||||
static auto raw_pkt_hdr_type = id::find_type<RecordType>("raw_pkt_hdr");
|
||||
static auto l2_hdr_type = id::find_type<RecordType>("l2_hdr");
|
||||
auto pkt_hdr = make_intrusive<RecordVal>(raw_pkt_hdr_type);
|
||||
auto l2_hdr = make_intrusive<RecordVal>(l2_hdr_type);
|
||||
RecordValPtr Packet::ToRawPktHdrVal() const {
|
||||
static auto raw_pkt_hdr_type = id::find_type<RecordType>("raw_pkt_hdr");
|
||||
static auto l2_hdr_type = id::find_type<RecordType>("l2_hdr");
|
||||
auto pkt_hdr = make_intrusive<RecordVal>(raw_pkt_hdr_type);
|
||||
auto l2_hdr = make_intrusive<RecordVal>(l2_hdr_type);
|
||||
|
||||
bool is_ethernet = link_type == DLT_EN10MB;
|
||||
bool is_ethernet = link_type == DLT_EN10MB;
|
||||
|
||||
int l3 = BifEnum::L3_UNKNOWN;
|
||||
int l3 = BifEnum::L3_UNKNOWN;
|
||||
|
||||
if ( l3_proto == L3_IPV4 )
|
||||
l3 = BifEnum::L3_IPV4;
|
||||
if ( l3_proto == L3_IPV4 )
|
||||
l3 = BifEnum::L3_IPV4;
|
||||
|
||||
else if ( l3_proto == L3_IPV6 )
|
||||
l3 = BifEnum::L3_IPV6;
|
||||
else if ( l3_proto == L3_IPV6 )
|
||||
l3 = BifEnum::L3_IPV6;
|
||||
|
||||
else if ( l3_proto == L3_ARP )
|
||||
l3 = BifEnum::L3_ARP;
|
||||
else if ( l3_proto == L3_ARP )
|
||||
l3 = BifEnum::L3_ARP;
|
||||
|
||||
// TODO: Get rid of hardcoded l3 protocols.
|
||||
// l2_hdr layout:
|
||||
// encap: link_encap; ##< L2 link encapsulation
|
||||
// len: count; ##< Total frame length on wire
|
||||
// cap_len: count; ##< Captured length
|
||||
// src: string &optional; ##< L2 source (if ethernet)
|
||||
// dst: string &optional; ##< L2 destination (if ethernet)
|
||||
// vlan: count &optional; ##< VLAN tag if any (and ethernet)
|
||||
// inner_vlan: count &optional; ##< Inner VLAN tag if any (and ethernet)
|
||||
// ethertype: count &optional; ##< If ethernet
|
||||
// proto: layer3_proto; ##< L3 proto
|
||||
// TODO: Get rid of hardcoded l3 protocols.
|
||||
// l2_hdr layout:
|
||||
// encap: link_encap; ##< L2 link encapsulation
|
||||
// len: count; ##< Total frame length on wire
|
||||
// cap_len: count; ##< Captured length
|
||||
// src: string &optional; ##< L2 source (if ethernet)
|
||||
// dst: string &optional; ##< L2 destination (if ethernet)
|
||||
// vlan: count &optional; ##< VLAN tag if any (and ethernet)
|
||||
// inner_vlan: count &optional; ##< Inner VLAN tag if any (and ethernet)
|
||||
// ethertype: count &optional; ##< If ethernet
|
||||
// proto: layer3_proto; ##< L3 proto
|
||||
|
||||
if ( is_ethernet )
|
||||
{
|
||||
// Ethernet header layout is:
|
||||
// dst[6bytes] src[6bytes] ethertype[2bytes]...
|
||||
l2_hdr->Assign(0, BifType::Enum::link_encap->GetEnumVal(BifEnum::LINK_ETHERNET));
|
||||
if ( is_ethernet ) {
|
||||
// Ethernet header layout is:
|
||||
// dst[6bytes] src[6bytes] ethertype[2bytes]...
|
||||
l2_hdr->Assign(0, BifType::Enum::link_encap->GetEnumVal(BifEnum::LINK_ETHERNET));
|
||||
|
||||
// FmtEUI48 needs at least 6 bytes to print out the mac address, plus 6 bytes for
|
||||
// skipping over the destination address.
|
||||
if ( cap_len >= 12 )
|
||||
l2_hdr->Assign(3, FmtEUI48(data + 6)); // src
|
||||
else
|
||||
l2_hdr->Assign(3, "00:00:00:00:00:00");
|
||||
// FmtEUI48 needs at least 6 bytes to print out the mac address, plus 6 bytes for
|
||||
// skipping over the destination address.
|
||||
if ( cap_len >= 12 )
|
||||
l2_hdr->Assign(3, FmtEUI48(data + 6)); // src
|
||||
else
|
||||
l2_hdr->Assign(3, "00:00:00:00:00:00");
|
||||
|
||||
if ( cap_len >= 6 )
|
||||
l2_hdr->Assign(4, FmtEUI48(data)); // dst
|
||||
else
|
||||
l2_hdr->Assign(4, "00:00:00:00:00:00");
|
||||
if ( cap_len >= 6 )
|
||||
l2_hdr->Assign(4, FmtEUI48(data)); // dst
|
||||
else
|
||||
l2_hdr->Assign(4, "00:00:00:00:00:00");
|
||||
|
||||
if ( vlan )
|
||||
l2_hdr->Assign(5, vlan);
|
||||
if ( vlan )
|
||||
l2_hdr->Assign(5, vlan);
|
||||
|
||||
if ( inner_vlan )
|
||||
l2_hdr->Assign(6, inner_vlan);
|
||||
if ( inner_vlan )
|
||||
l2_hdr->Assign(6, inner_vlan);
|
||||
|
||||
l2_hdr->Assign(7, eth_type);
|
||||
l2_hdr->Assign(7, eth_type);
|
||||
|
||||
if ( eth_type == ETHERTYPE_ARP || eth_type == ETHERTYPE_REVARP )
|
||||
// We also identify ARP for L3 over ethernet
|
||||
l3 = BifEnum::L3_ARP;
|
||||
}
|
||||
else
|
||||
l2_hdr->Assign(0, BifType::Enum::link_encap->GetEnumVal(BifEnum::LINK_UNKNOWN));
|
||||
if ( eth_type == ETHERTYPE_ARP || eth_type == ETHERTYPE_REVARP )
|
||||
// We also identify ARP for L3 over ethernet
|
||||
l3 = BifEnum::L3_ARP;
|
||||
}
|
||||
else
|
||||
l2_hdr->Assign(0, BifType::Enum::link_encap->GetEnumVal(BifEnum::LINK_UNKNOWN));
|
||||
|
||||
l2_hdr->Assign(1, len);
|
||||
l2_hdr->Assign(2, cap_len);
|
||||
l2_hdr->Assign(1, len);
|
||||
l2_hdr->Assign(2, cap_len);
|
||||
|
||||
l2_hdr->Assign(8, BifType::Enum::layer3_proto->GetEnumVal(l3));
|
||||
l2_hdr->Assign(8, BifType::Enum::layer3_proto->GetEnumVal(l3));
|
||||
|
||||
pkt_hdr->Assign(0, std::move(l2_hdr));
|
||||
pkt_hdr->Assign(0, std::move(l2_hdr));
|
||||
|
||||
if ( ip_hdr && cap_len >= ip_hdr->TotalLen() && (l3_proto == L3_IPV4 || l3_proto == L3_IPV6) )
|
||||
// Packet analysis will have stored the IP header in the packet, so we can use
|
||||
// that to build the output.
|
||||
return ip_hdr->ToPktHdrVal(std::move(pkt_hdr), 1);
|
||||
else
|
||||
return pkt_hdr;
|
||||
}
|
||||
if ( ip_hdr && cap_len >= ip_hdr->TotalLen() && (l3_proto == L3_IPV4 || l3_proto == L3_IPV6) )
|
||||
// Packet analysis will have stored the IP header in the packet, so we can use
|
||||
// that to build the output.
|
||||
return ip_hdr->ToPktHdrVal(std::move(pkt_hdr), 1);
|
||||
else
|
||||
return pkt_hdr;
|
||||
}
|
||||
|
||||
RecordValPtr Packet::ToVal(const Packet* p)
|
||||
{
|
||||
static auto pcap_packet = zeek::id::find_type<zeek::RecordType>("pcap_packet");
|
||||
auto val = zeek::make_intrusive<zeek::RecordVal>(pcap_packet);
|
||||
RecordValPtr Packet::ToVal(const Packet* p) {
|
||||
static auto pcap_packet = zeek::id::find_type<zeek::RecordType>("pcap_packet");
|
||||
auto val = zeek::make_intrusive<zeek::RecordVal>(pcap_packet);
|
||||
|
||||
if ( p )
|
||||
{
|
||||
val->Assign(0, static_cast<uint32_t>(p->ts.tv_sec));
|
||||
val->Assign(1, static_cast<uint32_t>(p->ts.tv_usec));
|
||||
val->Assign(2, p->cap_len);
|
||||
val->Assign(3, p->len);
|
||||
val->Assign(4, zeek::make_intrusive<zeek::StringVal>(p->cap_len, (const char*)p->data));
|
||||
val->Assign(5, zeek::BifType::Enum::link_encap->GetEnumVal(p->link_type));
|
||||
}
|
||||
else
|
||||
{
|
||||
val->Assign(0, 0);
|
||||
val->Assign(1, 0);
|
||||
val->Assign(2, 0);
|
||||
val->Assign(3, 0);
|
||||
val->Assign(4, zeek::val_mgr->EmptyString());
|
||||
val->Assign(5, zeek::BifType::Enum::link_encap->GetEnumVal(BifEnum::LINK_UNKNOWN));
|
||||
}
|
||||
if ( p ) {
|
||||
val->Assign(0, static_cast<uint32_t>(p->ts.tv_sec));
|
||||
val->Assign(1, static_cast<uint32_t>(p->ts.tv_usec));
|
||||
val->Assign(2, p->cap_len);
|
||||
val->Assign(3, p->len);
|
||||
val->Assign(4, zeek::make_intrusive<zeek::StringVal>(p->cap_len, (const char*)p->data));
|
||||
val->Assign(5, zeek::BifType::Enum::link_encap->GetEnumVal(p->link_type));
|
||||
}
|
||||
else {
|
||||
val->Assign(0, 0);
|
||||
val->Assign(1, 0);
|
||||
val->Assign(2, 0);
|
||||
val->Assign(3, 0);
|
||||
val->Assign(4, zeek::val_mgr->EmptyString());
|
||||
val->Assign(5, zeek::BifType::Enum::link_encap->GetEnumVal(BifEnum::LINK_UNKNOWN));
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
ValPtr Packet::FmtEUI48(const u_char* mac) const
|
||||
{
|
||||
char buf[20];
|
||||
snprintf(buf, sizeof buf, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3],
|
||||
mac[4], mac[5]);
|
||||
return make_intrusive<StringVal>(buf);
|
||||
}
|
||||
ValPtr Packet::FmtEUI48(const u_char* mac) const {
|
||||
char buf[20];
|
||||
snprintf(buf, sizeof buf, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
||||
return make_intrusive<StringVal>(buf);
|
||||
}
|
||||
|
||||
} // namespace zeek
|
||||
} // namespace zeek
|
||||
|
|
|
@ -22,14 +22,14 @@ using pkt_timeval = struct timeval;
|
|||
#include "zeek/TunnelEncapsulation.h"
|
||||
#include "zeek/session/Session.h"
|
||||
|
||||
namespace zeek
|
||||
{
|
||||
namespace zeek {
|
||||
|
||||
class ODesc;
|
||||
class Val;
|
||||
class RecordVal;
|
||||
|
||||
template <class T> class IntrusivePtr;
|
||||
template<class T>
|
||||
class IntrusivePtr;
|
||||
using ValPtr = IntrusivePtr<Val>;
|
||||
using RecordValPtr = IntrusivePtr<RecordVal>;
|
||||
|
||||
|
@ -38,254 +38,250 @@ using RecordValPtr = IntrusivePtr<RecordVal>;
|
|||
* This enum is sized as an int32_t to make the Packet structure align
|
||||
* correctly.
|
||||
*/
|
||||
enum Layer3Proto : int32_t
|
||||
{
|
||||
L3_UNKNOWN = -1, /// Layer 3 type could not be determined.
|
||||
L3_IPV4 = 1, /// Layer 3 is IPv4.
|
||||
L3_IPV6 = 2, /// Layer 3 is IPv6.
|
||||
L3_ARP = 3, /// Layer 3 is ARP.
|
||||
};
|
||||
enum Layer3Proto : int32_t {
|
||||
L3_UNKNOWN = -1, /// Layer 3 type could not be determined.
|
||||
L3_IPV4 = 1, /// Layer 3 is IPv4.
|
||||
L3_IPV6 = 2, /// Layer 3 is IPv6.
|
||||
L3_ARP = 3, /// Layer 3 is ARP.
|
||||
};
|
||||
|
||||
/**
|
||||
* A link-layer packet.
|
||||
*/
|
||||
class Packet
|
||||
{
|
||||
class Packet {
|
||||
public:
|
||||
/**
|
||||
* Construct and initialize from packet data.
|
||||
*
|
||||
* @param link_type The link type in the form of a \c DLT_* constant.
|
||||
*
|
||||
* @param ts The timestamp associated with the packet.
|
||||
*
|
||||
* @param caplen The number of bytes valid in *data*.
|
||||
*
|
||||
* @param len The wire length of the packet, which must be more or
|
||||
* equal *caplen* (but can't be less).
|
||||
*
|
||||
* @param data A pointer to the raw packet data, starting with the
|
||||
* layer 2 header. The pointer must remain valid for the lifetime of
|
||||
* the Packet instance, unless *copy* is true.
|
||||
*
|
||||
* @param copy If true, the constructor will make an internal copy of
|
||||
* *data*, so that the caller can release its version.
|
||||
*
|
||||
* @param tag A textual tag to associate with the packet for
|
||||
* differentiating the input streams.
|
||||
*/
|
||||
Packet(int link_type, pkt_timeval* ts, uint32_t caplen, uint32_t len, const u_char* data,
|
||||
bool copy = false, std::string tag = "")
|
||||
{
|
||||
Init(link_type, ts, caplen, len, data, copy, tag);
|
||||
}
|
||||
/**
|
||||
* Construct and initialize from packet data.
|
||||
*
|
||||
* @param link_type The link type in the form of a \c DLT_* constant.
|
||||
*
|
||||
* @param ts The timestamp associated with the packet.
|
||||
*
|
||||
* @param caplen The number of bytes valid in *data*.
|
||||
*
|
||||
* @param len The wire length of the packet, which must be more or
|
||||
* equal *caplen* (but can't be less).
|
||||
*
|
||||
* @param data A pointer to the raw packet data, starting with the
|
||||
* layer 2 header. The pointer must remain valid for the lifetime of
|
||||
* the Packet instance, unless *copy* is true.
|
||||
*
|
||||
* @param copy If true, the constructor will make an internal copy of
|
||||
* *data*, so that the caller can release its version.
|
||||
*
|
||||
* @param tag A textual tag to associate with the packet for
|
||||
* differentiating the input streams.
|
||||
*/
|
||||
Packet(int link_type, pkt_timeval* ts, uint32_t caplen, uint32_t len, const u_char* data, bool copy = false,
|
||||
std::string tag = "") {
|
||||
Init(link_type, ts, caplen, len, data, copy, tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor. For internal use only.
|
||||
*/
|
||||
Packet()
|
||||
{
|
||||
pkt_timeval ts = {0, 0};
|
||||
Init(0, &ts, 0, 0, nullptr);
|
||||
}
|
||||
/**
|
||||
* Default constructor. For internal use only.
|
||||
*/
|
||||
Packet() {
|
||||
pkt_timeval ts = {0, 0};
|
||||
Init(0, &ts, 0, 0, nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~Packet();
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~Packet();
|
||||
|
||||
/**
|
||||
* (Re-)initialize from packet data.
|
||||
*
|
||||
* @param link_type The link type in the form of a \c DLT_* constant.
|
||||
*
|
||||
* @param ts The timestamp associated with the packet.
|
||||
*
|
||||
* @param caplen The number of bytes valid in *data*.
|
||||
*
|
||||
* @param len The wire length of the packet, which must be more or
|
||||
* equal *caplen* (but can't be less).
|
||||
*
|
||||
* @param data A pointer to the raw packet data, starting with the
|
||||
* layer 2 header. The pointer must remain valid for the lifetime of
|
||||
* the Packet instance, unless *copy* is true.
|
||||
*
|
||||
* @param copy If true, the constructor will make an internal copy of
|
||||
* *data*, so that the caller can release its version.
|
||||
*
|
||||
* @param tag A textual tag to associate with the packet for
|
||||
* differentiating the input streams.
|
||||
*/
|
||||
void Init(int link_type, pkt_timeval* ts, uint32_t caplen, uint32_t len, const u_char* data,
|
||||
bool copy = false, std::string tag = "");
|
||||
/**
|
||||
* (Re-)initialize from packet data.
|
||||
*
|
||||
* @param link_type The link type in the form of a \c DLT_* constant.
|
||||
*
|
||||
* @param ts The timestamp associated with the packet.
|
||||
*
|
||||
* @param caplen The number of bytes valid in *data*.
|
||||
*
|
||||
* @param len The wire length of the packet, which must be more or
|
||||
* equal *caplen* (but can't be less).
|
||||
*
|
||||
* @param data A pointer to the raw packet data, starting with the
|
||||
* layer 2 header. The pointer must remain valid for the lifetime of
|
||||
* the Packet instance, unless *copy* is true.
|
||||
*
|
||||
* @param copy If true, the constructor will make an internal copy of
|
||||
* *data*, so that the caller can release its version.
|
||||
*
|
||||
* @param tag A textual tag to associate with the packet for
|
||||
* differentiating the input streams.
|
||||
*/
|
||||
void Init(int link_type, pkt_timeval* ts, uint32_t caplen, uint32_t len, const u_char* data, bool copy = false,
|
||||
std::string tag = "");
|
||||
|
||||
/**
|
||||
* Returns a \c raw_pkt_hdr RecordVal, which includes layer 2 and
|
||||
* also everything in IP_Hdr (i.e., IP4/6 + TCP/UDP/ICMP).
|
||||
*/
|
||||
RecordValPtr ToRawPktHdrVal() const;
|
||||
/**
|
||||
* Returns a \c raw_pkt_hdr RecordVal, which includes layer 2 and
|
||||
* also everything in IP_Hdr (i.e., IP4/6 + TCP/UDP/ICMP).
|
||||
*/
|
||||
RecordValPtr ToRawPktHdrVal() const;
|
||||
|
||||
/**
|
||||
* Returns a RecordVal that represents the Packet. This is used
|
||||
* by the get_current_packet bif.
|
||||
*/
|
||||
static RecordValPtr ToVal(const Packet* p);
|
||||
/**
|
||||
* Returns a RecordVal that represents the Packet. This is used
|
||||
* by the get_current_packet bif.
|
||||
*/
|
||||
static RecordValPtr ToVal(const Packet* p);
|
||||
|
||||
/**
|
||||
* Maximal length of a layer 2 address.
|
||||
*/
|
||||
static const int L2_ADDR_LEN = 6;
|
||||
/**
|
||||
* Maximal length of a layer 2 address.
|
||||
*/
|
||||
static const int L2_ADDR_LEN = 6;
|
||||
|
||||
/**
|
||||
* Empty layer 2 address to be used as default value. For example, the
|
||||
* LinuxSLL/LinuxSLL2 packet analyzers don't have a destination address
|
||||
* in the header and thus sets it to this default address.
|
||||
*/
|
||||
static constexpr const u_char L2_EMPTY_ADDR[L2_ADDR_LEN] = {0};
|
||||
/**
|
||||
* Empty layer 2 address to be used as default value. For example, the
|
||||
* LinuxSLL/LinuxSLL2 packet analyzers don't have a destination address
|
||||
* in the header and thus sets it to this default address.
|
||||
*/
|
||||
static constexpr const u_char L2_EMPTY_ADDR[L2_ADDR_LEN] = {0};
|
||||
|
||||
// These are passed in through the constructor.
|
||||
std::string tag; /// Used in serialization
|
||||
double time; /// Timestamp reconstituted as float
|
||||
pkt_timeval ts; /// Capture timestamp
|
||||
const u_char* data = nullptr; /// Packet data.
|
||||
uint32_t len; /// Actual length on wire
|
||||
uint32_t cap_len; /// Captured packet length
|
||||
uint32_t link_type; /// pcap link_type (DLT_EN10MB, DLT_RAW, etc)
|
||||
// These are passed in through the constructor.
|
||||
std::string tag; /// Used in serialization
|
||||
double time; /// Timestamp reconstituted as float
|
||||
pkt_timeval ts; /// Capture timestamp
|
||||
const u_char* data = nullptr; /// Packet data.
|
||||
uint32_t len; /// Actual length on wire
|
||||
uint32_t cap_len; /// Captured packet length
|
||||
uint32_t link_type; /// pcap link_type (DLT_EN10MB, DLT_RAW, etc)
|
||||
|
||||
/**
|
||||
* Layer 3 protocol identified (if any).
|
||||
*/
|
||||
Layer3Proto l3_proto;
|
||||
/**
|
||||
* Layer 3 protocol identified (if any).
|
||||
*/
|
||||
Layer3Proto l3_proto;
|
||||
|
||||
/**
|
||||
* If layer 2 is Ethernet, innermost ethertype field.
|
||||
*/
|
||||
uint32_t eth_type;
|
||||
/**
|
||||
* If layer 2 is Ethernet, innermost ethertype field.
|
||||
*/
|
||||
uint32_t eth_type;
|
||||
|
||||
/**
|
||||
* Layer 2 source address.
|
||||
*/
|
||||
const u_char* l2_src = nullptr;
|
||||
/**
|
||||
* Layer 2 source address.
|
||||
*/
|
||||
const u_char* l2_src = nullptr;
|
||||
|
||||
/**
|
||||
* Layer 2 destination address.
|
||||
*/
|
||||
const u_char* l2_dst = nullptr;
|
||||
/**
|
||||
* Layer 2 destination address.
|
||||
*/
|
||||
const u_char* l2_dst = nullptr;
|
||||
|
||||
/**
|
||||
* (Outermost) VLAN tag if any, else 0.
|
||||
*/
|
||||
uint32_t vlan = 0;
|
||||
/**
|
||||
* (Outermost) VLAN tag if any, else 0.
|
||||
*/
|
||||
uint32_t vlan = 0;
|
||||
|
||||
/**
|
||||
* (Innermost) VLAN tag if any, else 0.
|
||||
*/
|
||||
uint32_t inner_vlan = 0;
|
||||
/**
|
||||
* (Innermost) VLAN tag if any, else 0.
|
||||
*/
|
||||
uint32_t inner_vlan = 0;
|
||||
|
||||
/**
|
||||
* If this packet is related to a connection, this flag denotes whether
|
||||
* this packet is from the originator of the connection.
|
||||
*/
|
||||
bool is_orig = false;
|
||||
/**
|
||||
* If this packet is related to a connection, this flag denotes whether
|
||||
* this packet is from the originator of the connection.
|
||||
*/
|
||||
bool is_orig = false;
|
||||
|
||||
// Note: The following checksummed variables only apply to packets
|
||||
// received via a packet source, and not to packets contained inside
|
||||
// tunnels, etc.
|
||||
// Note: The following checksummed variables only apply to packets
|
||||
// received via a packet source, and not to packets contained inside
|
||||
// tunnels, etc.
|
||||
|
||||
/**
|
||||
* Indicates whether the data link layer/layer 2 checksum was validated
|
||||
* the hardware/kernel before being received by zeek.
|
||||
*/
|
||||
bool l2_checksummed = false;
|
||||
/**
|
||||
* Indicates whether the data link layer/layer 2 checksum was validated
|
||||
* the hardware/kernel before being received by zeek.
|
||||
*/
|
||||
bool l2_checksummed = false;
|
||||
|
||||
/**
|
||||
* Indicates whether the network layer/layer 3 checksum was validated by
|
||||
* the hardware/kernel before being received by zeek.
|
||||
*/
|
||||
bool l3_checksummed = false;
|
||||
/**
|
||||
* Indicates whether the network layer/layer 3 checksum was validated by
|
||||
* the hardware/kernel before being received by zeek.
|
||||
*/
|
||||
bool l3_checksummed = false;
|
||||
|
||||
/**
|
||||
* Indicates whether the transport layer/layer 4 checksum was validated
|
||||
* by the hardware/kernel before being received by zeek.
|
||||
*/
|
||||
bool l4_checksummed = false;
|
||||
/**
|
||||
* Indicates whether the transport layer/layer 4 checksum was validated
|
||||
* by the hardware/kernel before being received by zeek.
|
||||
*/
|
||||
bool l4_checksummed = false;
|
||||
|
||||
/**
|
||||
* Indicates whether this packet should be recorded.
|
||||
*/
|
||||
mutable bool dump_packet = false;
|
||||
/**
|
||||
* Indicates whether this packet should be recorded.
|
||||
*/
|
||||
mutable bool dump_packet = false;
|
||||
|
||||
/**
|
||||
* Indicates the amount of data to be dumped. If only a header is needed,
|
||||
* set this to the size of the header. Setting it to zero will dump the
|
||||
* entire packet.
|
||||
*/
|
||||
mutable int dump_size = 0;
|
||||
/**
|
||||
* Indicates the amount of data to be dumped. If only a header is needed,
|
||||
* set this to the size of the header. Setting it to zero will dump the
|
||||
* entire packet.
|
||||
*/
|
||||
mutable int dump_size = 0;
|
||||
|
||||
// These are fields passed between various packet analyzers. They're best
|
||||
// stored with the packet so they stay available as the packet is passed
|
||||
// around.
|
||||
// These are fields passed between various packet analyzers. They're best
|
||||
// stored with the packet so they stay available as the packet is passed
|
||||
// around.
|
||||
|
||||
/**
|
||||
* The stack of encapsulations this packet belongs to, if any. This is
|
||||
* used by the tunnel analyzers to keep track of the encapsulations as
|
||||
* processing occurs.
|
||||
*/
|
||||
std::shared_ptr<EncapsulationStack> encap = nullptr;
|
||||
/**
|
||||
* The stack of encapsulations this packet belongs to, if any. This is
|
||||
* used by the tunnel analyzers to keep track of the encapsulations as
|
||||
* processing occurs.
|
||||
*/
|
||||
std::shared_ptr<EncapsulationStack> encap = nullptr;
|
||||
|
||||
/**
|
||||
* The IP header for this packet. This is filled in by the IP analyzer
|
||||
* during processing if the packet contains an IP header.
|
||||
*/
|
||||
std::shared_ptr<IP_Hdr> ip_hdr = nullptr;
|
||||
/**
|
||||
* The IP header for this packet. This is filled in by the IP analyzer
|
||||
* during processing if the packet contains an IP header.
|
||||
*/
|
||||
std::shared_ptr<IP_Hdr> ip_hdr = nullptr;
|
||||
|
||||
/**
|
||||
* The protocol of the packet. This is used by the tunnel analyzers to
|
||||
* pass outer protocol from one level to the next.
|
||||
*/
|
||||
int proto = -1;
|
||||
/**
|
||||
* The protocol of the packet. This is used by the tunnel analyzers to
|
||||
* pass outer protocol from one level to the next.
|
||||
*/
|
||||
int proto = -1;
|
||||
|
||||
/**
|
||||
* If the packet contains a tunnel, this field will be filled in with
|
||||
* the type of tunnel. It is used to pass the tunnel type between the
|
||||
* packet analyzers during analysis.
|
||||
*/
|
||||
BifEnum::Tunnel::Type tunnel_type = BifEnum::Tunnel::NONE;
|
||||
/**
|
||||
* If the packet contains a tunnel, this field will be filled in with
|
||||
* the type of tunnel. It is used to pass the tunnel type between the
|
||||
* packet analyzers during analysis.
|
||||
*/
|
||||
BifEnum::Tunnel::Type tunnel_type = BifEnum::Tunnel::NONE;
|
||||
|
||||
/**
|
||||
* If the packet contains a GRE tunnel, this field will contain the
|
||||
* GRE version. It is used to pass this information from the GRE
|
||||
* analyzer to the IPTunnel analyzer.
|
||||
*/
|
||||
int gre_version = -1;
|
||||
/**
|
||||
* If the packet contains a GRE tunnel, this field will contain the
|
||||
* GRE version. It is used to pass this information from the GRE
|
||||
* analyzer to the IPTunnel analyzer.
|
||||
*/
|
||||
int gre_version = -1;
|
||||
|
||||
/**
|
||||
* If the packet contains a GRE tunnel, this field will contain the
|
||||
* GRE link type. It is used to pass this information from the GRE
|
||||
* analyzer to the IPTunnel analyzer.
|
||||
*/
|
||||
int gre_link_type = DLT_RAW;
|
||||
/**
|
||||
* If the packet contains a GRE tunnel, this field will contain the
|
||||
* GRE link type. It is used to pass this information from the GRE
|
||||
* analyzer to the IPTunnel analyzer.
|
||||
*/
|
||||
int gre_link_type = DLT_RAW;
|
||||
|
||||
/**
|
||||
* This flag indicates whether a packet has been processed. This can
|
||||
* mean different things depending on the traffic, but generally it
|
||||
* means that a packet has been logged in some way. We default to
|
||||
* false, and this can be set to true for any number of reasons.
|
||||
*/
|
||||
bool processed = false;
|
||||
/**
|
||||
* This flag indicates whether a packet has been processed. This can
|
||||
* mean different things depending on the traffic, but generally it
|
||||
* means that a packet has been logged in some way. We default to
|
||||
* false, and this can be set to true for any number of reasons.
|
||||
*/
|
||||
bool processed = false;
|
||||
|
||||
/**
|
||||
* The session related to this packet, if one exists.
|
||||
*/
|
||||
session::Session* session = nullptr;
|
||||
/**
|
||||
* The session related to this packet, if one exists.
|
||||
*/
|
||||
session::Session* session = nullptr;
|
||||
|
||||
private:
|
||||
// Renders an MAC address into its ASCII representation.
|
||||
ValPtr FmtEUI48(const u_char* mac) const;
|
||||
// Renders an MAC address into its ASCII representation.
|
||||
ValPtr FmtEUI48(const u_char* mac) const;
|
||||
|
||||
// True if we need to delete associated packet memory upon
|
||||
// destruction.
|
||||
bool copy;
|
||||
};
|
||||
// True if we need to delete associated packet memory upon
|
||||
// destruction.
|
||||
bool copy;
|
||||
};
|
||||
|
||||
} // namespace zeek
|
||||
} // namespace zeek
|
||||
|
|
|
@ -7,70 +7,43 @@
|
|||
|
||||
#include "zeek/DebugLogger.h"
|
||||
|
||||
namespace zeek::iosource
|
||||
{
|
||||
namespace zeek::iosource {
|
||||
|
||||
PktDumper::PktDumper()
|
||||
{
|
||||
is_open = false;
|
||||
errmsg = "";
|
||||
}
|
||||
PktDumper::PktDumper() {
|
||||
is_open = false;
|
||||
errmsg = "";
|
||||
}
|
||||
|
||||
void PktDumper::Init()
|
||||
{
|
||||
Open();
|
||||
}
|
||||
void PktDumper::Init() { Open(); }
|
||||
|
||||
void PktDumper::Done()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
void PktDumper::Done() { Close(); }
|
||||
|
||||
const std::string& PktDumper::Path() const
|
||||
{
|
||||
return props.path;
|
||||
}
|
||||
const std::string& PktDumper::Path() const { return props.path; }
|
||||
|
||||
bool PktDumper::IsOpen() const
|
||||
{
|
||||
return is_open;
|
||||
}
|
||||
bool PktDumper::IsOpen() const { return is_open; }
|
||||
|
||||
double PktDumper::OpenTime() const
|
||||
{
|
||||
return is_open ? props.open_time : 0;
|
||||
}
|
||||
double PktDumper::OpenTime() const { return is_open ? props.open_time : 0; }
|
||||
|
||||
bool PktDumper::IsError() const
|
||||
{
|
||||
return ! errmsg.empty();
|
||||
}
|
||||
bool PktDumper::IsError() const { return ! errmsg.empty(); }
|
||||
|
||||
const char* PktDumper::ErrorMsg() const
|
||||
{
|
||||
return errmsg.size() ? errmsg.c_str() : nullptr;
|
||||
}
|
||||
const char* PktDumper::ErrorMsg() const { return errmsg.size() ? errmsg.c_str() : nullptr; }
|
||||
|
||||
void PktDumper::Opened(const Properties& arg_props)
|
||||
{
|
||||
is_open = true;
|
||||
props = arg_props;
|
||||
DBG_LOG(DBG_PKTIO, "Opened dumper %s", props.path.c_str());
|
||||
}
|
||||
void PktDumper::Opened(const Properties& arg_props) {
|
||||
is_open = true;
|
||||
props = arg_props;
|
||||
DBG_LOG(DBG_PKTIO, "Opened dumper %s", props.path.c_str());
|
||||
}
|
||||
|
||||
void PktDumper::Closed()
|
||||
{
|
||||
is_open = false;
|
||||
DBG_LOG(DBG_PKTIO, "Closed dumper %s", props.path.c_str());
|
||||
props.path = "";
|
||||
}
|
||||
void PktDumper::Closed() {
|
||||
is_open = false;
|
||||
DBG_LOG(DBG_PKTIO, "Closed dumper %s", props.path.c_str());
|
||||
props.path = "";
|
||||
}
|
||||
|
||||
void PktDumper::Error(const std::string& msg)
|
||||
{
|
||||
errmsg = msg;
|
||||
void PktDumper::Error(const std::string& msg) {
|
||||
errmsg = msg;
|
||||
|
||||
DBG_LOG(DBG_PKTIO, "Error with dumper %s: %s", IsOpen() ? props.path.c_str() : "<not open>",
|
||||
msg.c_str());
|
||||
}
|
||||
DBG_LOG(DBG_PKTIO, "Error with dumper %s: %s", IsOpen() ? props.path.c_str() : "<not open>", msg.c_str());
|
||||
}
|
||||
|
||||
} // namespace zeek::iosource
|
||||
} // namespace zeek::iosource
|
||||
|
|
|
@ -6,138 +6,134 @@
|
|||
|
||||
#include <string>
|
||||
|
||||
namespace zeek
|
||||
{
|
||||
namespace zeek {
|
||||
|
||||
class Packet;
|
||||
|
||||
namespace iosource
|
||||
{
|
||||
namespace iosource {
|
||||
|
||||
/**
|
||||
* Base class for packet dumpers.
|
||||
*/
|
||||
class PktDumper
|
||||
{
|
||||
class PktDumper {
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
PktDumper();
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
PktDumper();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~PktDumper() = default;
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
virtual ~PktDumper() = default;
|
||||
|
||||
/**
|
||||
* Returns the path associated with the dumper.
|
||||
*/
|
||||
const std::string& Path() const;
|
||||
/**
|
||||
* Returns the path associated with the dumper.
|
||||
*/
|
||||
const std::string& Path() const;
|
||||
|
||||
/**
|
||||
* Returns true if the dumper is open for writing.
|
||||
*/
|
||||
bool IsOpen() const;
|
||||
/**
|
||||
* Returns true if the dumper is open for writing.
|
||||
*/
|
||||
bool IsOpen() const;
|
||||
|
||||
/**
|
||||
* Returns the time when the dumper was opened for writing.
|
||||
*/
|
||||
double OpenTime() const;
|
||||
/**
|
||||
* Returns the time when the dumper was opened for writing.
|
||||
*/
|
||||
double OpenTime() const;
|
||||
|
||||
/**
|
||||
* Returns returns true if the dumper has encountered an error.
|
||||
*/
|
||||
bool IsError() const;
|
||||
/**
|
||||
* Returns returns true if the dumper has encountered an error.
|
||||
*/
|
||||
bool IsError() const;
|
||||
|
||||
/**
|
||||
* Returns if the dumper has encountered an error, returns a
|
||||
* corresponding error message. Returns an empty string otherwise.
|
||||
*/
|
||||
const char* ErrorMsg() const;
|
||||
/**
|
||||
* Returns if the dumper has encountered an error, returns a
|
||||
* corresponding error message. Returns an empty string otherwise.
|
||||
*/
|
||||
const char* ErrorMsg() const;
|
||||
|
||||
// PktDumper interface for derived classes to implement.
|
||||
// PktDumper interface for derived classes to implement.
|
||||
|
||||
/**
|
||||
* Called by the manager system to open the source.
|
||||
*
|
||||
* Derived classes must implement this method. If successful, the
|
||||
* implementation must call \a Opened(); if not, it must call Error()
|
||||
* with a corresponding message.
|
||||
*/
|
||||
virtual void Open() = 0;
|
||||
/**
|
||||
* Called by the manager system to open the source.
|
||||
*
|
||||
* Derived classes must implement this method. If successful, the
|
||||
* implementation must call \a Opened(); if not, it must call Error()
|
||||
* with a corresponding message.
|
||||
*/
|
||||
virtual void Open() = 0;
|
||||
|
||||
/**
|
||||
* Called by the manager system to close the dumper.
|
||||
*
|
||||
* Derived classes must implement this method. If successful, the
|
||||
* implementation must call \a Closed(); if not, it must call Error()
|
||||
* with a corresponding message.
|
||||
*/
|
||||
virtual void Close() = 0;
|
||||
/**
|
||||
* Called by the manager system to close the dumper.
|
||||
*
|
||||
* Derived classes must implement this method. If successful, the
|
||||
* implementation must call \a Closed(); if not, it must call Error()
|
||||
* with a corresponding message.
|
||||
*/
|
||||
virtual void Close() = 0;
|
||||
|
||||
/**
|
||||
* Called to write a packet to the dumper.
|
||||
*
|
||||
* Derived classes must implement this method.
|
||||
*
|
||||
* @param pkt The packet to record.
|
||||
*
|
||||
* @return True if successful, false otherwise (in which case \a
|
||||
* Error() must have been called.)
|
||||
*/
|
||||
virtual bool Dump(const Packet* pkt) = 0;
|
||||
/**
|
||||
* Called to write a packet to the dumper.
|
||||
*
|
||||
* Derived classes must implement this method.
|
||||
*
|
||||
* @param pkt The packet to record.
|
||||
*
|
||||
* @return True if successful, false otherwise (in which case \a
|
||||
* Error() must have been called.)
|
||||
*/
|
||||
virtual bool Dump(const Packet* pkt) = 0;
|
||||
|
||||
protected:
|
||||
friend class Manager;
|
||||
friend class Manager;
|
||||
|
||||
/**
|
||||
* Structure to pass back information about the packet dumper to the
|
||||
* base class. Derived class pass an instance of this to \a Opened().
|
||||
*/
|
||||
struct Properties
|
||||
{
|
||||
std::string path;
|
||||
double open_time = 0.0;
|
||||
};
|
||||
/**
|
||||
* Structure to pass back information about the packet dumper to the
|
||||
* base class. Derived class pass an instance of this to \a Opened().
|
||||
*/
|
||||
struct Properties {
|
||||
std::string path;
|
||||
double open_time = 0.0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called from the implementations of \a Open() to signal that the
|
||||
* source has been successfully opened.
|
||||
*
|
||||
* @param props A properties instance describing the now open source.
|
||||
*/
|
||||
void Opened(const Properties& props);
|
||||
/**
|
||||
* Called from the implementations of \a Open() to signal that the
|
||||
* source has been successfully opened.
|
||||
*
|
||||
* @param props A properties instance describing the now open source.
|
||||
*/
|
||||
void Opened(const Properties& props);
|
||||
|
||||
/**
|
||||
* Called from the implementations of \a Close() to signal that the
|
||||
* source has been closed.
|
||||
*/
|
||||
void Closed();
|
||||
/**
|
||||
* Called from the implementations of \a Close() to signal that the
|
||||
* source has been closed.
|
||||
*/
|
||||
void Closed();
|
||||
|
||||
/**
|
||||
* Called from derived classes to signal an error.
|
||||
*
|
||||
* @param msg A corresponding error message.
|
||||
*/
|
||||
void Error(const std::string& msg);
|
||||
/**
|
||||
* Called from derived classes to signal an error.
|
||||
*
|
||||
* @param msg A corresponding error message.
|
||||
*/
|
||||
void Error(const std::string& msg);
|
||||
|
||||
/**
|
||||
* Called by the manager to initialize the dumper.
|
||||
*/
|
||||
void Init();
|
||||
/**
|
||||
* Called by the manager to initialize the dumper.
|
||||
*/
|
||||
void Init();
|
||||
|
||||
/**
|
||||
* Called by the manager to shutdown the dumper.
|
||||
*/
|
||||
void Done();
|
||||
/**
|
||||
* Called by the manager to shutdown the dumper.
|
||||
*/
|
||||
void Done();
|
||||
|
||||
private:
|
||||
bool is_open;
|
||||
Properties props;
|
||||
bool is_open;
|
||||
Properties props;
|
||||
|
||||
std::string errmsg;
|
||||
};
|
||||
std::string errmsg;
|
||||
};
|
||||
|
||||
} // namespace iosource
|
||||
} // namespace zeek
|
||||
} // namespace iosource
|
||||
} // namespace zeek
|
||||
|
|
|
@ -16,319 +16,260 @@
|
|||
#include "zeek/session/Manager.h"
|
||||
#include "zeek/util.h"
|
||||
|
||||
namespace zeek::iosource
|
||||
{
|
||||
|
||||
PktSrc::Properties::Properties()
|
||||
{
|
||||
selectable_fd = -1;
|
||||
link_type = -1;
|
||||
netmask = NETMASK_UNKNOWN;
|
||||
is_live = false;
|
||||
}
|
||||
|
||||
PktSrc::PktSrc()
|
||||
{
|
||||
have_packet = false;
|
||||
// Small lie to make a new PktSrc look like the previous ExtractNextPacket() was successful.
|
||||
had_packet = true;
|
||||
errbuf = "";
|
||||
SetClosed(true);
|
||||
}
|
||||
|
||||
PktSrc::~PktSrc()
|
||||
{
|
||||
for ( auto code : filters )
|
||||
delete code;
|
||||
}
|
||||
|
||||
const std::string& PktSrc::Path() const
|
||||
{
|
||||
static std::string not_open("not open");
|
||||
return IsOpen() ? props.path : not_open;
|
||||
}
|
||||
|
||||
const char* PktSrc::ErrorMsg() const
|
||||
{
|
||||
return errbuf.size() ? errbuf.c_str() : nullptr;
|
||||
}
|
||||
|
||||
int PktSrc::LinkType() const
|
||||
{
|
||||
return IsOpen() ? props.link_type : -1;
|
||||
}
|
||||
|
||||
uint32_t PktSrc::Netmask() const
|
||||
{
|
||||
return IsOpen() ? props.netmask : NETMASK_UNKNOWN;
|
||||
}
|
||||
|
||||
bool PktSrc::IsError() const
|
||||
{
|
||||
return ! errbuf.empty();
|
||||
}
|
||||
|
||||
bool PktSrc::IsLive() const
|
||||
{
|
||||
return props.is_live;
|
||||
}
|
||||
|
||||
void PktSrc::Opened(const Properties& arg_props)
|
||||
{
|
||||
props = arg_props;
|
||||
SetClosed(false);
|
||||
|
||||
if ( ! PrecompileFilter(0, "") || ! SetFilter(0) )
|
||||
{
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( props.is_live )
|
||||
Info(util::fmt("listening on %s\n", props.path.c_str()));
|
||||
|
||||
if ( props.selectable_fd != -1 )
|
||||
if ( ! iosource_mgr->RegisterFd(props.selectable_fd, this) )
|
||||
reporter->FatalError("Failed to register pktsrc fd with iosource_mgr");
|
||||
|
||||
DBG_LOG(DBG_PKTIO, "Opened source %s", props.path.c_str());
|
||||
}
|
||||
|
||||
void PktSrc::Closed()
|
||||
{
|
||||
SetClosed(true);
|
||||
|
||||
if ( props.selectable_fd != -1 )
|
||||
iosource_mgr->UnregisterFd(props.selectable_fd, this);
|
||||
|
||||
DBG_LOG(DBG_PKTIO, "Closed source %s", props.path.c_str());
|
||||
}
|
||||
|
||||
void PktSrc::Error(const std::string& msg)
|
||||
{
|
||||
// We don't report this immediately, Zeek will ask us for the error
|
||||
// once it notices we aren't open.
|
||||
errbuf = msg;
|
||||
DBG_LOG(DBG_PKTIO, "Error with source %s: %s", IsOpen() ? props.path.c_str() : "<not open>",
|
||||
msg.c_str());
|
||||
}
|
||||
|
||||
void PktSrc::Info(const std::string& msg)
|
||||
{
|
||||
reporter->Info("%s", msg.c_str());
|
||||
}
|
||||
|
||||
void PktSrc::Weird(const std::string& msg, const Packet* p)
|
||||
{
|
||||
session_mgr->Weird(msg.c_str(), p);
|
||||
}
|
||||
|
||||
void PktSrc::InternalError(const std::string& msg)
|
||||
{
|
||||
reporter->InternalError("%s", msg.c_str());
|
||||
}
|
||||
|
||||
void PktSrc::InitSource()
|
||||
{
|
||||
Open();
|
||||
}
|
||||
|
||||
void PktSrc::Done()
|
||||
{
|
||||
if ( IsOpen() )
|
||||
Close();
|
||||
}
|
||||
|
||||
bool PktSrc::HasBeenIdleFor(double interval) const
|
||||
{
|
||||
if ( have_packet || had_packet )
|
||||
return false;
|
||||
|
||||
// Take the hit of a current_time() call now.
|
||||
double now = zeek::util::current_time(true);
|
||||
return idle_at_wallclock < now - interval;
|
||||
};
|
||||
|
||||
void PktSrc::Process()
|
||||
{
|
||||
if ( ! IsOpen() )
|
||||
return;
|
||||
|
||||
if ( ! ExtractNextPacketInternal() )
|
||||
return;
|
||||
|
||||
run_state::detail::dispatch_packet(¤t_packet, this);
|
||||
|
||||
have_packet = false;
|
||||
DoneWithPacket();
|
||||
}
|
||||
|
||||
const char* PktSrc::Tag()
|
||||
{
|
||||
return "PktSrc";
|
||||
}
|
||||
|
||||
bool PktSrc::ExtractNextPacketInternal()
|
||||
{
|
||||
if ( have_packet )
|
||||
return true;
|
||||
|
||||
have_packet = false;
|
||||
|
||||
// Don't return any packets if processing is suspended (except for the
|
||||
// very first packet which we need to set up times).
|
||||
if ( run_state::is_processing_suspended() && run_state::detail::first_timestamp )
|
||||
return false;
|
||||
|
||||
if ( run_state::pseudo_realtime )
|
||||
run_state::detail::current_wallclock = util::current_time(true);
|
||||
|
||||
if ( ExtractNextPacket(¤t_packet) )
|
||||
{
|
||||
had_packet = true;
|
||||
|
||||
if ( current_packet.time < 0 )
|
||||
{
|
||||
Weird("negative_packet_timestamp", ¤t_packet);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! run_state::detail::first_timestamp )
|
||||
run_state::detail::first_timestamp = current_packet.time;
|
||||
|
||||
have_packet = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update the idle_at timestamp the first time we've failed
|
||||
// to extract a packet. This assumes ExtractNextPacket() is
|
||||
// called regularly which is true for non-selectable PktSrc
|
||||
// instances, but even for selectable ones with an FD the
|
||||
// main-loop will call Process() on the interface regularly
|
||||
// and detect it as idle.
|
||||
if ( had_packet )
|
||||
{
|
||||
DBG_LOG(DBG_PKTIO, "source %s is idle now", props.path.c_str());
|
||||
idle_at_wallclock = zeek::util::current_time(true);
|
||||
}
|
||||
|
||||
had_packet = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
detail::BPF_Program* PktSrc::CompileFilter(const std::string& filter)
|
||||
{
|
||||
auto code = std::make_unique<detail::BPF_Program>();
|
||||
|
||||
if ( ! code->Compile(BifConst::Pcap::snaplen, LinkType(), filter.c_str(), Netmask()) )
|
||||
{
|
||||
std::string msg = util::fmt("cannot compile BPF filter \"%s\"", filter.c_str());
|
||||
|
||||
std::string state_msg = code->GetStateMessage();
|
||||
if ( ! state_msg.empty() )
|
||||
msg += ": " + state_msg;
|
||||
|
||||
Error(msg);
|
||||
}
|
||||
|
||||
return code.release();
|
||||
}
|
||||
|
||||
bool PktSrc::PrecompileBPFFilter(int index, const std::string& filter)
|
||||
{
|
||||
if ( index < 0 )
|
||||
return false;
|
||||
|
||||
// Compile filter. This will always return a pointer, but may have stored an error
|
||||
// internally.
|
||||
auto code = CompileFilter(filter);
|
||||
|
||||
// Store it in vector.
|
||||
if ( index >= static_cast<int>(filters.size()) )
|
||||
filters.resize(index + 1);
|
||||
|
||||
if ( auto* old = filters[index] )
|
||||
delete old;
|
||||
|
||||
filters[index] = code;
|
||||
|
||||
return code->GetState() != FilterState::FATAL;
|
||||
}
|
||||
|
||||
detail::BPF_Program* PktSrc::GetBPFFilter(int index)
|
||||
{
|
||||
if ( index < 0 )
|
||||
return nullptr;
|
||||
|
||||
return (static_cast<int>(filters.size()) > index ? filters[index] : nullptr);
|
||||
}
|
||||
|
||||
bool PktSrc::ApplyBPFFilter(int index, const struct pcap_pkthdr* hdr, const u_char* pkt)
|
||||
{
|
||||
detail::BPF_Program* code = GetBPFFilter(index);
|
||||
|
||||
if ( ! code )
|
||||
{
|
||||
Error(util::fmt("BPF filter %d not compiled", index));
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( code->MatchesAnything() )
|
||||
return true;
|
||||
|
||||
return pcap_offline_filter(code->GetProgram(), hdr, pkt);
|
||||
}
|
||||
|
||||
bool PktSrc::GetCurrentPacket(const Packet** pkt)
|
||||
{
|
||||
if ( ! have_packet )
|
||||
return false;
|
||||
|
||||
*pkt = ¤t_packet;
|
||||
return true;
|
||||
}
|
||||
|
||||
double PktSrc::GetNextTimeout()
|
||||
{
|
||||
if ( run_state::is_processing_suspended() )
|
||||
return -1;
|
||||
|
||||
// If we're in pseudo-realtime mode, find the next time that a packet is ready
|
||||
// and have poll block until then.
|
||||
if ( run_state::pseudo_realtime )
|
||||
{
|
||||
ExtractNextPacketInternal();
|
||||
|
||||
// This duplicates the calculation used in run_state::check_pseudo_time().
|
||||
double pseudo_time = current_packet.time - run_state::detail::first_timestamp;
|
||||
double ct = (util::current_time(true) - run_state::detail::first_wallclock) *
|
||||
run_state::pseudo_realtime;
|
||||
return std::max(0.0, pseudo_time - ct);
|
||||
}
|
||||
|
||||
// If there's no file descriptor for the source, which is the case for some interfaces
|
||||
// like myricom, we can't rely on the polling mechanism to wait for data to be
|
||||
// available. As gross as it is, just spin with a short timeout here so that it will
|
||||
// continually poll the interface. The old IOSource code had a 20 microsecond timeout
|
||||
// between calls to select() so just use that.
|
||||
|
||||
// A heuristic to avoid short sleeps when a non-selectable packet source has more
|
||||
// packets queued is to return 0.0 if the source has yielded a packet on the
|
||||
// last call to ExtractNextPacket().
|
||||
if ( props.selectable_fd == -1 )
|
||||
{
|
||||
if ( have_packet || had_packet )
|
||||
return 0.0;
|
||||
|
||||
return BifConst::Pcap::non_fd_timeout;
|
||||
}
|
||||
|
||||
// If there's an FD (offline or live) we want poll to do what it has to with it.
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
} // namespace zeek::iosource
|
||||
namespace zeek::iosource {
|
||||
|
||||
PktSrc::Properties::Properties() {
|
||||
selectable_fd = -1;
|
||||
link_type = -1;
|
||||
netmask = NETMASK_UNKNOWN;
|
||||
is_live = false;
|
||||
}
|
||||
|
||||
PktSrc::PktSrc() {
|
||||
have_packet = false;
|
||||
// Small lie to make a new PktSrc look like the previous ExtractNextPacket() was successful.
|
||||
had_packet = true;
|
||||
errbuf = "";
|
||||
SetClosed(true);
|
||||
}
|
||||
|
||||
PktSrc::~PktSrc() {
|
||||
for ( auto code : filters )
|
||||
delete code;
|
||||
}
|
||||
|
||||
const std::string& PktSrc::Path() const {
|
||||
static std::string not_open("not open");
|
||||
return IsOpen() ? props.path : not_open;
|
||||
}
|
||||
|
||||
const char* PktSrc::ErrorMsg() const { return errbuf.size() ? errbuf.c_str() : nullptr; }
|
||||
|
||||
int PktSrc::LinkType() const { return IsOpen() ? props.link_type : -1; }
|
||||
|
||||
uint32_t PktSrc::Netmask() const { return IsOpen() ? props.netmask : NETMASK_UNKNOWN; }
|
||||
|
||||
bool PktSrc::IsError() const { return ! errbuf.empty(); }
|
||||
|
||||
bool PktSrc::IsLive() const { return props.is_live; }
|
||||
|
||||
void PktSrc::Opened(const Properties& arg_props) {
|
||||
props = arg_props;
|
||||
SetClosed(false);
|
||||
|
||||
if ( ! PrecompileFilter(0, "") || ! SetFilter(0) ) {
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( props.is_live )
|
||||
Info(util::fmt("listening on %s\n", props.path.c_str()));
|
||||
|
||||
if ( props.selectable_fd != -1 )
|
||||
if ( ! iosource_mgr->RegisterFd(props.selectable_fd, this) )
|
||||
reporter->FatalError("Failed to register pktsrc fd with iosource_mgr");
|
||||
|
||||
DBG_LOG(DBG_PKTIO, "Opened source %s", props.path.c_str());
|
||||
}
|
||||
|
||||
void PktSrc::Closed() {
|
||||
SetClosed(true);
|
||||
|
||||
if ( props.selectable_fd != -1 )
|
||||
iosource_mgr->UnregisterFd(props.selectable_fd, this);
|
||||
|
||||
DBG_LOG(DBG_PKTIO, "Closed source %s", props.path.c_str());
|
||||
}
|
||||
|
||||
void PktSrc::Error(const std::string& msg) {
|
||||
// We don't report this immediately, Zeek will ask us for the error
|
||||
// once it notices we aren't open.
|
||||
errbuf = msg;
|
||||
DBG_LOG(DBG_PKTIO, "Error with source %s: %s", IsOpen() ? props.path.c_str() : "<not open>", msg.c_str());
|
||||
}
|
||||
|
||||
void PktSrc::Info(const std::string& msg) { reporter->Info("%s", msg.c_str()); }
|
||||
|
||||
void PktSrc::Weird(const std::string& msg, const Packet* p) { session_mgr->Weird(msg.c_str(), p); }
|
||||
|
||||
void PktSrc::InternalError(const std::string& msg) { reporter->InternalError("%s", msg.c_str()); }
|
||||
|
||||
void PktSrc::InitSource() { Open(); }
|
||||
|
||||
void PktSrc::Done() {
|
||||
if ( IsOpen() )
|
||||
Close();
|
||||
}
|
||||
|
||||
bool PktSrc::HasBeenIdleFor(double interval) const {
|
||||
if ( have_packet || had_packet )
|
||||
return false;
|
||||
|
||||
// Take the hit of a current_time() call now.
|
||||
double now = zeek::util::current_time(true);
|
||||
return idle_at_wallclock < now - interval;
|
||||
};
|
||||
|
||||
void PktSrc::Process() {
|
||||
if ( ! IsOpen() )
|
||||
return;
|
||||
|
||||
if ( ! ExtractNextPacketInternal() )
|
||||
return;
|
||||
|
||||
run_state::detail::dispatch_packet(¤t_packet, this);
|
||||
|
||||
have_packet = false;
|
||||
DoneWithPacket();
|
||||
}
|
||||
|
||||
const char* PktSrc::Tag() { return "PktSrc"; }
|
||||
|
||||
bool PktSrc::ExtractNextPacketInternal() {
|
||||
if ( have_packet )
|
||||
return true;
|
||||
|
||||
have_packet = false;
|
||||
|
||||
// Don't return any packets if processing is suspended (except for the
|
||||
// very first packet which we need to set up times).
|
||||
if ( run_state::is_processing_suspended() && run_state::detail::first_timestamp )
|
||||
return false;
|
||||
|
||||
if ( run_state::pseudo_realtime )
|
||||
run_state::detail::current_wallclock = util::current_time(true);
|
||||
|
||||
if ( ExtractNextPacket(¤t_packet) ) {
|
||||
had_packet = true;
|
||||
|
||||
if ( current_packet.time < 0 ) {
|
||||
Weird("negative_packet_timestamp", ¤t_packet);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! run_state::detail::first_timestamp )
|
||||
run_state::detail::first_timestamp = current_packet.time;
|
||||
|
||||
have_packet = true;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
// Update the idle_at timestamp the first time we've failed
|
||||
// to extract a packet. This assumes ExtractNextPacket() is
|
||||
// called regularly which is true for non-selectable PktSrc
|
||||
// instances, but even for selectable ones with an FD the
|
||||
// main-loop will call Process() on the interface regularly
|
||||
// and detect it as idle.
|
||||
if ( had_packet ) {
|
||||
DBG_LOG(DBG_PKTIO, "source %s is idle now", props.path.c_str());
|
||||
idle_at_wallclock = zeek::util::current_time(true);
|
||||
}
|
||||
|
||||
had_packet = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
detail::BPF_Program* PktSrc::CompileFilter(const std::string& filter) {
|
||||
auto code = std::make_unique<detail::BPF_Program>();
|
||||
|
||||
if ( ! code->Compile(BifConst::Pcap::snaplen, LinkType(), filter.c_str(), Netmask()) ) {
|
||||
std::string msg = util::fmt("cannot compile BPF filter \"%s\"", filter.c_str());
|
||||
|
||||
std::string state_msg = code->GetStateMessage();
|
||||
if ( ! state_msg.empty() )
|
||||
msg += ": " + state_msg;
|
||||
|
||||
Error(msg);
|
||||
}
|
||||
|
||||
return code.release();
|
||||
}
|
||||
|
||||
bool PktSrc::PrecompileBPFFilter(int index, const std::string& filter) {
|
||||
if ( index < 0 )
|
||||
return false;
|
||||
|
||||
// Compile filter. This will always return a pointer, but may have stored an error
|
||||
// internally.
|
||||
auto code = CompileFilter(filter);
|
||||
|
||||
// Store it in vector.
|
||||
if ( index >= static_cast<int>(filters.size()) )
|
||||
filters.resize(index + 1);
|
||||
|
||||
if ( auto* old = filters[index] )
|
||||
delete old;
|
||||
|
||||
filters[index] = code;
|
||||
|
||||
return code->GetState() != FilterState::FATAL;
|
||||
}
|
||||
|
||||
detail::BPF_Program* PktSrc::GetBPFFilter(int index) {
|
||||
if ( index < 0 )
|
||||
return nullptr;
|
||||
|
||||
return (static_cast<int>(filters.size()) > index ? filters[index] : nullptr);
|
||||
}
|
||||
|
||||
bool PktSrc::ApplyBPFFilter(int index, const struct pcap_pkthdr* hdr, const u_char* pkt) {
|
||||
detail::BPF_Program* code = GetBPFFilter(index);
|
||||
|
||||
if ( ! code ) {
|
||||
Error(util::fmt("BPF filter %d not compiled", index));
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( code->MatchesAnything() )
|
||||
return true;
|
||||
|
||||
return pcap_offline_filter(code->GetProgram(), hdr, pkt);
|
||||
}
|
||||
|
||||
bool PktSrc::GetCurrentPacket(const Packet** pkt) {
|
||||
if ( ! have_packet )
|
||||
return false;
|
||||
|
||||
*pkt = ¤t_packet;
|
||||
return true;
|
||||
}
|
||||
|
||||
double PktSrc::GetNextTimeout() {
|
||||
if ( run_state::is_processing_suspended() )
|
||||
return -1;
|
||||
|
||||
// If we're in pseudo-realtime mode, find the next time that a packet is ready
|
||||
// and have poll block until then.
|
||||
if ( run_state::pseudo_realtime ) {
|
||||
ExtractNextPacketInternal();
|
||||
|
||||
// This duplicates the calculation used in run_state::check_pseudo_time().
|
||||
double pseudo_time = current_packet.time - run_state::detail::first_timestamp;
|
||||
double ct = (util::current_time(true) - run_state::detail::first_wallclock) * run_state::pseudo_realtime;
|
||||
return std::max(0.0, pseudo_time - ct);
|
||||
}
|
||||
|
||||
// If there's no file descriptor for the source, which is the case for some interfaces
|
||||
// like myricom, we can't rely on the polling mechanism to wait for data to be
|
||||
// available. As gross as it is, just spin with a short timeout here so that it will
|
||||
// continually poll the interface. The old IOSource code had a 20 microsecond timeout
|
||||
// between calls to select() so just use that.
|
||||
|
||||
// A heuristic to avoid short sleeps when a non-selectable packet source has more
|
||||
// packets queued is to return 0.0 if the source has yielded a packet on the
|
||||
// last call to ExtractNextPacket().
|
||||
if ( props.selectable_fd == -1 ) {
|
||||
if ( have_packet || had_packet )
|
||||
return 0.0;
|
||||
|
||||
return BifConst::Pcap::non_fd_timeout;
|
||||
}
|
||||
|
||||
// If there's an FD (offline or live) we want poll to do what it has to with it.
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
} // namespace zeek::iosource
|
||||
|
|
|
@ -12,386 +12,379 @@
|
|||
|
||||
struct pcap_pkthdr;
|
||||
|
||||
namespace zeek::iosource
|
||||
{
|
||||
namespace zeek::iosource {
|
||||
|
||||
/**
|
||||
* Base class for packet sources.
|
||||
*/
|
||||
class PktSrc : public IOSource
|
||||
{
|
||||
class PktSrc : public IOSource {
|
||||
public:
|
||||
static const uint32_t NETMASK_UNKNOWN = 0xffffffff;
|
||||
static const uint32_t NETMASK_UNKNOWN = 0xffffffff;
|
||||
|
||||
/**
|
||||
* Struct for returning statistics on a packet source.
|
||||
*/
|
||||
struct Stats
|
||||
{
|
||||
/**
|
||||
* Packets received by source after filtering (w/o drops).
|
||||
*/
|
||||
uint64_t received = 0;
|
||||
/**
|
||||
* Struct for returning statistics on a packet source.
|
||||
*/
|
||||
struct Stats {
|
||||
/**
|
||||
* Packets received by source after filtering (w/o drops).
|
||||
*/
|
||||
uint64_t received = 0;
|
||||
|
||||
/**
|
||||
* Packets dropped by source.
|
||||
*/
|
||||
uint64_t dropped = 0; // pkts dropped
|
||||
/**
|
||||
* Packets dropped by source.
|
||||
*/
|
||||
uint64_t dropped = 0; // pkts dropped
|
||||
|
||||
/**
|
||||
* Total number of packets on link before filtering.
|
||||
* Optional, can be left unset if not available.
|
||||
*/
|
||||
uint64_t link = 0;
|
||||
/**
|
||||
* Total number of packets on link before filtering.
|
||||
* Optional, can be left unset if not available.
|
||||
*/
|
||||
uint64_t link = 0;
|
||||
|
||||
/**
|
||||
* Bytes received by source after filtering (w/o drops).
|
||||
*/
|
||||
uint64_t bytes_received = 0;
|
||||
/**
|
||||
* Bytes received by source after filtering (w/o drops).
|
||||
*/
|
||||
uint64_t bytes_received = 0;
|
||||
|
||||
/**
|
||||
* Packets filtered by the packet source.
|
||||
*/
|
||||
std::optional<uint64_t> filtered;
|
||||
};
|
||||
/**
|
||||
* Packets filtered by the packet source.
|
||||
*/
|
||||
std::optional<uint64_t> filtered;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
PktSrc();
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
PktSrc();
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~PktSrc() override;
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~PktSrc() override;
|
||||
|
||||
/**
|
||||
* Returns the path associated with the source. This is the interface
|
||||
* name for live source, and a filename for offline sources.
|
||||
*/
|
||||
const std::string& Path() const;
|
||||
/**
|
||||
* Returns the path associated with the source. This is the interface
|
||||
* name for live source, and a filename for offline sources.
|
||||
*/
|
||||
const std::string& Path() const;
|
||||
|
||||
/**
|
||||
* Returns true if this is a live source.
|
||||
*/
|
||||
bool IsLive() const;
|
||||
/**
|
||||
* Returns true if this is a live source.
|
||||
*/
|
||||
bool IsLive() const;
|
||||
|
||||
/**
|
||||
* Returns the link type of the source.
|
||||
*/
|
||||
int LinkType() const;
|
||||
/**
|
||||
* Returns the link type of the source.
|
||||
*/
|
||||
int LinkType() const;
|
||||
|
||||
/**
|
||||
* Returns the netmask associated with the source, or \c
|
||||
* NETMASK_UNKNOWN if unknown.
|
||||
*/
|
||||
uint32_t Netmask() const;
|
||||
/**
|
||||
* Returns the netmask associated with the source, or \c
|
||||
* NETMASK_UNKNOWN if unknown.
|
||||
*/
|
||||
uint32_t Netmask() const;
|
||||
|
||||
/**
|
||||
* Returns true if the source has flagged an error.
|
||||
*/
|
||||
bool IsError() const;
|
||||
/**
|
||||
* Returns true if the source has flagged an error.
|
||||
*/
|
||||
bool IsError() const;
|
||||
|
||||
/**
|
||||
* Return true if the source has been observed idle for the given
|
||||
* wallclock interval.
|
||||
*
|
||||
* The default implementation looks at failing ExtractNextPacket() calls
|
||||
* and keeps the wallclock timestamp when there was no packet available.
|
||||
* The source is considered idle when there has not been a packet since
|
||||
* \a interval seconds.
|
||||
*
|
||||
* Alternative implementations may check internally buffered packets
|
||||
* or queue lengths.
|
||||
*
|
||||
* @param interval Interval in seconds.
|
||||
*
|
||||
* @return True if the source has been idle for \a interval seconds.
|
||||
*/
|
||||
virtual bool HasBeenIdleFor(double interval) const;
|
||||
/**
|
||||
* Return true if the source has been observed idle for the given
|
||||
* wallclock interval.
|
||||
*
|
||||
* The default implementation looks at failing ExtractNextPacket() calls
|
||||
* and keeps the wallclock timestamp when there was no packet available.
|
||||
* The source is considered idle when there has not been a packet since
|
||||
* \a interval seconds.
|
||||
*
|
||||
* Alternative implementations may check internally buffered packets
|
||||
* or queue lengths.
|
||||
*
|
||||
* @param interval Interval in seconds.
|
||||
*
|
||||
* @return True if the source has been idle for \a interval seconds.
|
||||
*/
|
||||
virtual bool HasBeenIdleFor(double interval) const;
|
||||
|
||||
/**
|
||||
* If the source encountered an error, returns a corresponding error
|
||||
* message. Returns an empty string otherwise.
|
||||
*/
|
||||
const char* ErrorMsg() const;
|
||||
/**
|
||||
* If the source encountered an error, returns a corresponding error
|
||||
* message. Returns an empty string otherwise.
|
||||
*/
|
||||
const char* ErrorMsg() const;
|
||||
|
||||
/**
|
||||
* Precompiles a BPF filter and associates the given index with it.
|
||||
* The compiled filter will be then available via \a GetBPFFilter().
|
||||
*
|
||||
* This is primarily a helper for packet source implementations that
|
||||
* want to apply BPF filtering to their packets.
|
||||
*
|
||||
* @param index The index to associate with the filter.
|
||||
*
|
||||
* @param BPF filter The filter string to precompile.
|
||||
*
|
||||
* @return True on success, false if a problem occurred.
|
||||
*/
|
||||
virtual bool PrecompileBPFFilter(int index, const std::string& filter);
|
||||
/**
|
||||
* Precompiles a BPF filter and associates the given index with it.
|
||||
* The compiled filter will be then available via \a GetBPFFilter().
|
||||
*
|
||||
* This is primarily a helper for packet source implementations that
|
||||
* want to apply BPF filtering to their packets.
|
||||
*
|
||||
* @param index The index to associate with the filter.
|
||||
*
|
||||
* @param BPF filter The filter string to precompile.
|
||||
*
|
||||
* @return True on success, false if a problem occurred.
|
||||
*/
|
||||
virtual bool PrecompileBPFFilter(int index, const std::string& filter);
|
||||
|
||||
/**
|
||||
* Returns the precompiled BPF filter associated with a given index,
|
||||
* if any, as compiled by \a PrecompileBPFFilter().
|
||||
*
|
||||
* This is primarily a helper for packet source implementation that
|
||||
* want to apply BPF filtering to their packets.
|
||||
*
|
||||
* @return The BPF filter associated, or null if none has been
|
||||
* (successfully) compiled.
|
||||
*/
|
||||
detail::BPF_Program* GetBPFFilter(int index);
|
||||
/**
|
||||
* Returns the precompiled BPF filter associated with a given index,
|
||||
* if any, as compiled by \a PrecompileBPFFilter().
|
||||
*
|
||||
* This is primarily a helper for packet source implementation that
|
||||
* want to apply BPF filtering to their packets.
|
||||
*
|
||||
* @return The BPF filter associated, or null if none has been
|
||||
* (successfully) compiled.
|
||||
*/
|
||||
detail::BPF_Program* GetBPFFilter(int index);
|
||||
|
||||
/**
|
||||
* Applies a precompiled BPF filter to a packet. This will close the
|
||||
* source with an error message if no filter with that index has been
|
||||
* compiled.
|
||||
*
|
||||
* This is primarily a helper for packet source implementation that
|
||||
* want to apply BPF filtering to their packets.
|
||||
*
|
||||
* @param index The index of the filter to apply.
|
||||
*
|
||||
* @param hdr The header of the packet to filter.
|
||||
*
|
||||
* @param pkt The content of the packet to filter.
|
||||
*
|
||||
* @return True if it matches.
|
||||
*/
|
||||
bool ApplyBPFFilter(int index, const struct pcap_pkthdr* hdr, const u_char* pkt);
|
||||
/**
|
||||
* Applies a precompiled BPF filter to a packet. This will close the
|
||||
* source with an error message if no filter with that index has been
|
||||
* compiled.
|
||||
*
|
||||
* This is primarily a helper for packet source implementation that
|
||||
* want to apply BPF filtering to their packets.
|
||||
*
|
||||
* @param index The index of the filter to apply.
|
||||
*
|
||||
* @param hdr The header of the packet to filter.
|
||||
*
|
||||
* @param pkt The content of the packet to filter.
|
||||
*
|
||||
* @return True if it matches.
|
||||
*/
|
||||
bool ApplyBPFFilter(int index, const struct pcap_pkthdr* hdr, const u_char* pkt);
|
||||
|
||||
/**
|
||||
* Returns the packet currently being processed, if available.
|
||||
*
|
||||
* @param pkt A pointer to pass the content of the current packet
|
||||
* back.
|
||||
*
|
||||
* @return True if the current packet is available, or false if not.
|
||||
*/
|
||||
bool GetCurrentPacket(const Packet** hdr);
|
||||
/**
|
||||
* Returns the packet currently being processed, if available.
|
||||
*
|
||||
* @param pkt A pointer to pass the content of the current packet
|
||||
* back.
|
||||
*
|
||||
* @return True if the current packet is available, or false if not.
|
||||
*/
|
||||
bool GetCurrentPacket(const Packet** hdr);
|
||||
|
||||
// PacketSource interface for derived classes to override.
|
||||
// PacketSource interface for derived classes to override.
|
||||
|
||||
/**
|
||||
* Precompiles a filter and associates a given index with it. The
|
||||
* filter syntax is defined by the packet source's implementation.
|
||||
*
|
||||
* Derived classes can override this method to implement their own
|
||||
* filtering. If not overridden, it uses the pcap-based BPF filtering
|
||||
* by default.
|
||||
*
|
||||
* @param index The index to associate with the filter
|
||||
*
|
||||
* @param filter The filter string to precompile.
|
||||
*
|
||||
* @return True on success, false if a problem occurred or filtering
|
||||
* is not supported.
|
||||
*/
|
||||
virtual bool PrecompileFilter(int index, const std::string& filter)
|
||||
{
|
||||
return PrecompileBPFFilter(index, filter);
|
||||
}
|
||||
/**
|
||||
* Precompiles a filter and associates a given index with it. The
|
||||
* filter syntax is defined by the packet source's implementation.
|
||||
*
|
||||
* Derived classes can override this method to implement their own
|
||||
* filtering. If not overridden, it uses the pcap-based BPF filtering
|
||||
* by default.
|
||||
*
|
||||
* @param index The index to associate with the filter
|
||||
*
|
||||
* @param filter The filter string to precompile.
|
||||
*
|
||||
* @return True on success, false if a problem occurred or filtering
|
||||
* is not supported.
|
||||
*/
|
||||
virtual bool PrecompileFilter(int index, const std::string& filter) { return PrecompileBPFFilter(index, filter); }
|
||||
|
||||
/**
|
||||
* Activates a precompiled filter with the given index.
|
||||
*
|
||||
* Derived classes must implement this to implement their filtering.
|
||||
* If they want to use BPF but don't support it natively, they can
|
||||
* call the corresponding helper method provided by \a PktSrc.
|
||||
*
|
||||
* @param index The index of the filter to activate.
|
||||
*
|
||||
* @return True on success, false if a problem occurred or the
|
||||
* filtering is not supported.
|
||||
*/
|
||||
virtual bool SetFilter(int index) = 0;
|
||||
/**
|
||||
* Activates a precompiled filter with the given index.
|
||||
*
|
||||
* Derived classes must implement this to implement their filtering.
|
||||
* If they want to use BPF but don't support it natively, they can
|
||||
* call the corresponding helper method provided by \a PktSrc.
|
||||
*
|
||||
* @param index The index of the filter to activate.
|
||||
*
|
||||
* @return True on success, false if a problem occurred or the
|
||||
* filtering is not supported.
|
||||
*/
|
||||
virtual bool SetFilter(int index) = 0;
|
||||
|
||||
/**
|
||||
* Returns current statistics about the source.
|
||||
*
|
||||
* Derived classes must implement this method.
|
||||
*
|
||||
* @param stats A statistics structure that the method fill out.
|
||||
*/
|
||||
virtual void Statistics(Stats* stats) = 0;
|
||||
/**
|
||||
* Returns current statistics about the source.
|
||||
*
|
||||
* Derived classes must implement this method.
|
||||
*
|
||||
* @param stats A statistics structure that the method fill out.
|
||||
*/
|
||||
virtual void Statistics(Stats* stats) = 0;
|
||||
|
||||
/**
|
||||
* Return the next timeout value for this source. This should be
|
||||
* overridden by source classes where they have a timeout value
|
||||
* that can wake up the poll.
|
||||
*
|
||||
* @return A value for the next time that the source thinks the
|
||||
* poll should time out in seconds from the current time. Return
|
||||
* -1 if this should not be considered.
|
||||
*/
|
||||
virtual double GetNextTimeout() override;
|
||||
/**
|
||||
* Return the next timeout value for this source. This should be
|
||||
* overridden by source classes where they have a timeout value
|
||||
* that can wake up the poll.
|
||||
*
|
||||
* @return A value for the next time that the source thinks the
|
||||
* poll should time out in seconds from the current time. Return
|
||||
* -1 if this should not be considered.
|
||||
*/
|
||||
virtual double GetNextTimeout() override;
|
||||
|
||||
protected:
|
||||
friend class Manager;
|
||||
friend class Manager;
|
||||
|
||||
// Methods to use by derived classes.
|
||||
// Methods to use by derived classes.
|
||||
|
||||
/**
|
||||
* Structure to pass back information about the packet source to the
|
||||
* base class. Derived class pass an instance of this to \a Opened().
|
||||
*/
|
||||
struct Properties
|
||||
{
|
||||
/**
|
||||
* The path associated with the source. This is the interface
|
||||
* name for live source, and a filename for offline sources.
|
||||
*/
|
||||
std::string path;
|
||||
/**
|
||||
* Structure to pass back information about the packet source to the
|
||||
* base class. Derived class pass an instance of this to \a Opened().
|
||||
*/
|
||||
struct Properties {
|
||||
/**
|
||||
* The path associated with the source. This is the interface
|
||||
* name for live source, and a filename for offline sources.
|
||||
*/
|
||||
std::string path;
|
||||
|
||||
/**
|
||||
* A file descriptor suitable to use with \a select() for
|
||||
* determining if there's input available from this source.
|
||||
*/
|
||||
int selectable_fd;
|
||||
/**
|
||||
* A file descriptor suitable to use with \a select() for
|
||||
* determining if there's input available from this source.
|
||||
*/
|
||||
int selectable_fd;
|
||||
|
||||
/**
|
||||
* The link type for packets from this source.
|
||||
*/
|
||||
int link_type;
|
||||
/**
|
||||
* The link type for packets from this source.
|
||||
*/
|
||||
int link_type;
|
||||
|
||||
/**
|
||||
* Returns the netmask associated with the source, or \c
|
||||
* NETMASK_UNKNOWN if unknown.
|
||||
*/
|
||||
uint32_t netmask;
|
||||
/**
|
||||
* Returns the netmask associated with the source, or \c
|
||||
* NETMASK_UNKNOWN if unknown.
|
||||
*/
|
||||
uint32_t netmask;
|
||||
|
||||
/**
|
||||
* True if the source is reading live input, false for
|
||||
* working offline.
|
||||
*/
|
||||
bool is_live;
|
||||
/**
|
||||
* True if the source is reading live input, false for
|
||||
* working offline.
|
||||
*/
|
||||
bool is_live;
|
||||
|
||||
Properties();
|
||||
};
|
||||
Properties();
|
||||
};
|
||||
|
||||
/**
|
||||
* Called from the implementations of \a Open() to signal that the
|
||||
* source has been successfully opened.
|
||||
*
|
||||
* @param props A properties instance describing the now open source.
|
||||
*/
|
||||
void Opened(const Properties& props);
|
||||
/**
|
||||
* Called from the implementations of \a Open() to signal that the
|
||||
* source has been successfully opened.
|
||||
*
|
||||
* @param props A properties instance describing the now open source.
|
||||
*/
|
||||
void Opened(const Properties& props);
|
||||
|
||||
/**
|
||||
* Called from the implementations of \a Close() to signal that the
|
||||
* source has been closed.
|
||||
*/
|
||||
void Closed();
|
||||
/**
|
||||
* Called from the implementations of \a Close() to signal that the
|
||||
* source has been closed.
|
||||
*/
|
||||
void Closed();
|
||||
|
||||
/**
|
||||
* Can be called from derived classes to send an informational
|
||||
* message to the user.
|
||||
*
|
||||
* @param msg The message to pass on.
|
||||
*/
|
||||
void Info(const std::string& msg);
|
||||
/**
|
||||
* Can be called from derived classes to send an informational
|
||||
* message to the user.
|
||||
*
|
||||
* @param msg The message to pass on.
|
||||
*/
|
||||
void Info(const std::string& msg);
|
||||
|
||||
/**
|
||||
* Can be called from derived classes to flag send an error.
|
||||
*
|
||||
* @param msg The message going with the error.
|
||||
*/
|
||||
void Error(const std::string& msg);
|
||||
/**
|
||||
* Can be called from derived classes to flag send an error.
|
||||
*
|
||||
* @param msg The message going with the error.
|
||||
*/
|
||||
void Error(const std::string& msg);
|
||||
|
||||
/**
|
||||
* Can be called from derived classes to flag a "weird" situation.
|
||||
*
|
||||
* @param msg The message to pass on.
|
||||
*
|
||||
* @param pkt The packet associated with the weird, or null if none.
|
||||
*/
|
||||
void Weird(const std::string& msg, const Packet* pkt);
|
||||
/**
|
||||
* Can be called from derived classes to flag a "weird" situation.
|
||||
*
|
||||
* @param msg The message to pass on.
|
||||
*
|
||||
* @param pkt The packet associated with the weird, or null if none.
|
||||
*/
|
||||
void Weird(const std::string& msg, const Packet* pkt);
|
||||
|
||||
/**
|
||||
* Can be called from derived classes to flag an internal error,
|
||||
* which will abort execution.
|
||||
*
|
||||
* @param msg The message to pass on.
|
||||
*/
|
||||
void InternalError(const std::string& msg);
|
||||
/**
|
||||
* Can be called from derived classes to flag an internal error,
|
||||
* which will abort execution.
|
||||
*
|
||||
* @param msg The message to pass on.
|
||||
*/
|
||||
void InternalError(const std::string& msg);
|
||||
|
||||
// PktSrc interface for derived classes to implement.
|
||||
// PktSrc interface for derived classes to implement.
|
||||
|
||||
/**
|
||||
* Called by the manager system to open the source.
|
||||
*
|
||||
* Derived classes must implement this method. If successful, the
|
||||
* implementation must call \a Opened(); if not, it must call Error()
|
||||
* with a corresponding message.
|
||||
*/
|
||||
virtual void Open() = 0;
|
||||
/**
|
||||
* Called by the manager system to open the source.
|
||||
*
|
||||
* Derived classes must implement this method. If successful, the
|
||||
* implementation must call \a Opened(); if not, it must call Error()
|
||||
* with a corresponding message.
|
||||
*/
|
||||
virtual void Open() = 0;
|
||||
|
||||
/**
|
||||
* Called by the manager system to close the source.
|
||||
*
|
||||
* Derived classes must implement this method. If successful, the
|
||||
* implementation must call \a Closed(); if not, it must call Error()
|
||||
* with a corresponding message.
|
||||
*/
|
||||
virtual void Close() = 0;
|
||||
/**
|
||||
* Called by the manager system to close the source.
|
||||
*
|
||||
* Derived classes must implement this method. If successful, the
|
||||
* implementation must call \a Closed(); if not, it must call Error()
|
||||
* with a corresponding message.
|
||||
*/
|
||||
virtual void Close() = 0;
|
||||
|
||||
/**
|
||||
* Provides the next packet from the source.
|
||||
*
|
||||
* @param pkt The packet structure to fill in with the packet's
|
||||
* information. The callee keep ownership of the data but must
|
||||
* guarantee that it stays available at least until \a
|
||||
* DoneWithPacket() is called. It is guaranteed that no two calls to
|
||||
* this method will happen with \a DoneWithPacket() in between.
|
||||
*
|
||||
* @return True if a packet is available and *pkt* filled in. False
|
||||
* if not packet is available or an error occurred (which must be
|
||||
* flagged via Error()).
|
||||
*/
|
||||
virtual bool ExtractNextPacket(Packet* pkt) = 0;
|
||||
/**
|
||||
* Provides the next packet from the source.
|
||||
*
|
||||
* @param pkt The packet structure to fill in with the packet's
|
||||
* information. The callee keep ownership of the data but must
|
||||
* guarantee that it stays available at least until \a
|
||||
* DoneWithPacket() is called. It is guaranteed that no two calls to
|
||||
* this method will happen with \a DoneWithPacket() in between.
|
||||
*
|
||||
* @return True if a packet is available and *pkt* filled in. False
|
||||
* if not packet is available or an error occurred (which must be
|
||||
* flagged via Error()).
|
||||
*/
|
||||
virtual bool ExtractNextPacket(Packet* pkt) = 0;
|
||||
|
||||
/**
|
||||
* Signals that the data of previously extracted packet will no
|
||||
* longer be needed.
|
||||
*/
|
||||
virtual void DoneWithPacket() = 0;
|
||||
/**
|
||||
* Signals that the data of previously extracted packet will no
|
||||
* longer be needed.
|
||||
*/
|
||||
virtual void DoneWithPacket() = 0;
|
||||
|
||||
/**
|
||||
* Performs the actual filter compilation. This can be overridden to
|
||||
* provide a different implementation of the compilation called by
|
||||
* PrecompileBPFFilter(). This is primarily used by the pcap source
|
||||
* use a different version of BPF_Filter::Compile;
|
||||
*
|
||||
* @param filter the filtering string being compiled.
|
||||
*
|
||||
* @return The compiled filter or nullptr if compilation failed.
|
||||
*/
|
||||
virtual detail::BPF_Program* CompileFilter(const std::string& filter);
|
||||
/**
|
||||
* Performs the actual filter compilation. This can be overridden to
|
||||
* provide a different implementation of the compilation called by
|
||||
* PrecompileBPFFilter(). This is primarily used by the pcap source
|
||||
* use a different version of BPF_Filter::Compile;
|
||||
*
|
||||
* @param filter the filtering string being compiled.
|
||||
*
|
||||
* @return The compiled filter or nullptr if compilation failed.
|
||||
*/
|
||||
virtual detail::BPF_Program* CompileFilter(const std::string& filter);
|
||||
|
||||
private:
|
||||
// Internal helper for ExtractNextPacket().
|
||||
bool ExtractNextPacketInternal();
|
||||
// Internal helper for ExtractNextPacket().
|
||||
bool ExtractNextPacketInternal();
|
||||
|
||||
// IOSource interface implementation.
|
||||
void InitSource() override;
|
||||
void Done() override;
|
||||
void Process() override;
|
||||
const char* Tag() override;
|
||||
// IOSource interface implementation.
|
||||
void InitSource() override;
|
||||
void Done() override;
|
||||
void Process() override;
|
||||
const char* Tag() override;
|
||||
|
||||
Properties props;
|
||||
Properties props;
|
||||
|
||||
bool have_packet;
|
||||
Packet current_packet;
|
||||
// Did the previous call to ExtractNextPacket() yield a packet.
|
||||
bool had_packet;
|
||||
bool have_packet;
|
||||
Packet current_packet;
|
||||
// Did the previous call to ExtractNextPacket() yield a packet.
|
||||
bool had_packet;
|
||||
|
||||
double idle_at_wallclock = 0.0;
|
||||
double idle_at_wallclock = 0.0;
|
||||
|
||||
// For BPF filtering support.
|
||||
std::vector<detail::BPF_Program*> filters;
|
||||
// For BPF filtering support.
|
||||
std::vector<detail::BPF_Program*> filters;
|
||||
|
||||
std::string errbuf;
|
||||
};
|
||||
std::string errbuf;
|
||||
};
|
||||
|
||||
} // namespace zeek::iosource
|
||||
} // namespace zeek::iosource
|
||||
|
|
|
@ -9,112 +9,98 @@
|
|||
#include "zeek/iosource/PktSrc.h"
|
||||
#include "zeek/iosource/pcap/pcap.bif.h"
|
||||
|
||||
namespace zeek::iosource::pcap
|
||||
{
|
||||
namespace zeek::iosource::pcap {
|
||||
|
||||
PcapDumper::PcapDumper(const std::string& path, bool arg_append)
|
||||
{
|
||||
append = arg_append;
|
||||
props.path = path;
|
||||
dumper = nullptr;
|
||||
pd = nullptr;
|
||||
}
|
||||
PcapDumper::PcapDumper(const std::string& path, bool arg_append) {
|
||||
append = arg_append;
|
||||
props.path = path;
|
||||
dumper = nullptr;
|
||||
pd = nullptr;
|
||||
}
|
||||
|
||||
void PcapDumper::Open()
|
||||
{
|
||||
int linktype = -1;
|
||||
void PcapDumper::Open() {
|
||||
int linktype = -1;
|
||||
|
||||
pd = pcap_open_dead(DLT_EN10MB, BifConst::Pcap::snaplen);
|
||||
pd = pcap_open_dead(DLT_EN10MB, BifConst::Pcap::snaplen);
|
||||
|
||||
if ( ! pd )
|
||||
{
|
||||
Error("error for pcap_open_dead");
|
||||
return;
|
||||
}
|
||||
if ( ! pd ) {
|
||||
Error("error for pcap_open_dead");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( props.path.empty() )
|
||||
{
|
||||
Error("no filename given");
|
||||
return;
|
||||
}
|
||||
if ( props.path.empty() ) {
|
||||
Error("no filename given");
|
||||
return;
|
||||
}
|
||||
|
||||
struct stat s;
|
||||
int exists = 0;
|
||||
struct stat s;
|
||||
int exists = 0;
|
||||
|
||||
if ( append )
|
||||
{
|
||||
// See if output file already exists (and is non-empty).
|
||||
exists = stat(props.path.c_str(), &s);
|
||||
if ( append ) {
|
||||
// See if output file already exists (and is non-empty).
|
||||
exists = stat(props.path.c_str(), &s);
|
||||
|
||||
if ( exists < 0 && errno != ENOENT )
|
||||
{
|
||||
Error(util::fmt("can't stat file %s: %s", props.path.c_str(), strerror(errno)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( exists < 0 && errno != ENOENT ) {
|
||||
Error(util::fmt("can't stat file %s: %s", props.path.c_str(), strerror(errno)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! append || exists < 0 || s.st_size == 0 )
|
||||
{
|
||||
// Open new file.
|
||||
dumper = pcap_dump_open(pd, props.path.c_str());
|
||||
if ( ! dumper )
|
||||
{
|
||||
Error(pcap_geterr(pd));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( ! append || exists < 0 || s.st_size == 0 ) {
|
||||
// Open new file.
|
||||
dumper = pcap_dump_open(pd, props.path.c_str());
|
||||
if ( ! dumper ) {
|
||||
Error(pcap_geterr(pd));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
else {
|
||||
#ifdef HAVE_PCAP_DUMP_OPEN_APPEND
|
||||
dumper = pcap_dump_open_append(pd, props.path.c_str());
|
||||
dumper = pcap_dump_open_append(pd, props.path.c_str());
|
||||
#else
|
||||
// Old file and we need to append, which, unfortunately,
|
||||
// is not supported by libpcap. So, we have to hack a
|
||||
// little bit, knowing that pcap_dumper_t is, in fact,
|
||||
// a FILE ... :-(
|
||||
dumper = (pcap_dumper_t*)fopen(props.path.c_str(), "a");
|
||||
// Old file and we need to append, which, unfortunately,
|
||||
// is not supported by libpcap. So, we have to hack a
|
||||
// little bit, knowing that pcap_dumper_t is, in fact,
|
||||
// a FILE ... :-(
|
||||
dumper = (pcap_dumper_t*)fopen(props.path.c_str(), "a");
|
||||
#endif
|
||||
if ( ! dumper )
|
||||
{
|
||||
Error(util::fmt("can't open dump %s: %s", props.path.c_str(), strerror(errno)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( ! dumper ) {
|
||||
Error(util::fmt("can't open dump %s: %s", props.path.c_str(), strerror(errno)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
props.open_time = run_state::network_time;
|
||||
Opened(props);
|
||||
}
|
||||
props.open_time = run_state::network_time;
|
||||
Opened(props);
|
||||
}
|
||||
|
||||
void PcapDumper::Close()
|
||||
{
|
||||
if ( ! dumper )
|
||||
return;
|
||||
void PcapDumper::Close() {
|
||||
if ( ! dumper )
|
||||
return;
|
||||
|
||||
pcap_dump_close(dumper);
|
||||
pcap_close(pd);
|
||||
dumper = nullptr;
|
||||
pd = nullptr;
|
||||
pcap_dump_close(dumper);
|
||||
pcap_close(pd);
|
||||
dumper = nullptr;
|
||||
pd = nullptr;
|
||||
|
||||
Closed();
|
||||
}
|
||||
Closed();
|
||||
}
|
||||
|
||||
bool PcapDumper::Dump(const Packet* pkt)
|
||||
{
|
||||
if ( ! dumper )
|
||||
return false;
|
||||
bool PcapDumper::Dump(const Packet* pkt) {
|
||||
if ( ! dumper )
|
||||
return false;
|
||||
|
||||
// Reconstitute the pcap_pkthdr.
|
||||
const struct pcap_pkthdr phdr = {pkt->ts, pkt->cap_len, pkt->len};
|
||||
// Reconstitute the pcap_pkthdr.
|
||||
const struct pcap_pkthdr phdr = {pkt->ts, pkt->cap_len, pkt->len};
|
||||
|
||||
pcap_dump((u_char*)dumper, &phdr, pkt->data);
|
||||
pcap_dump_flush(dumper);
|
||||
return true;
|
||||
}
|
||||
pcap_dump((u_char*)dumper, &phdr, pkt->data);
|
||||
pcap_dump_flush(dumper);
|
||||
return true;
|
||||
}
|
||||
|
||||
iosource::PktDumper* PcapDumper::Instantiate(const std::string& path, bool append)
|
||||
{
|
||||
return new PcapDumper(path, append);
|
||||
}
|
||||
iosource::PktDumper* PcapDumper::Instantiate(const std::string& path, bool append) {
|
||||
return new PcapDumper(path, append);
|
||||
}
|
||||
|
||||
} // namespace zeek::iosource::pcap
|
||||
} // namespace zeek::iosource::pcap
|
||||
|
|
|
@ -4,36 +4,33 @@
|
|||
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
extern "C" {
|
||||
#include <pcap.h>
|
||||
}
|
||||
}
|
||||
|
||||
#include "zeek/iosource/PktDumper.h"
|
||||
|
||||
namespace zeek::iosource::pcap
|
||||
{
|
||||
namespace zeek::iosource::pcap {
|
||||
|
||||
class PcapDumper : public PktDumper
|
||||
{
|
||||
class PcapDumper : public PktDumper {
|
||||
public:
|
||||
PcapDumper(const std::string& path, bool append);
|
||||
~PcapDumper() override = default;
|
||||
PcapDumper(const std::string& path, bool append);
|
||||
~PcapDumper() override = default;
|
||||
|
||||
static PktDumper* Instantiate(const std::string& path, bool append);
|
||||
static PktDumper* Instantiate(const std::string& path, bool append);
|
||||
|
||||
protected:
|
||||
// PktDumper interface.
|
||||
void Open() override;
|
||||
void Close() override;
|
||||
bool Dump(const Packet* pkt) override;
|
||||
// PktDumper interface.
|
||||
void Open() override;
|
||||
void Close() override;
|
||||
bool Dump(const Packet* pkt) override;
|
||||
|
||||
private:
|
||||
Properties props;
|
||||
Properties props;
|
||||
|
||||
bool append;
|
||||
pcap_dumper_t* dumper;
|
||||
pcap_t* pd;
|
||||
};
|
||||
bool append;
|
||||
pcap_dumper_t* dumper;
|
||||
pcap_t* pd;
|
||||
};
|
||||
|
||||
} // namespace zeek::iosource::pcap
|
||||
} // namespace zeek::iosource::pcap
|
||||
|
|
|
@ -6,25 +6,20 @@
|
|||
#include "zeek/iosource/pcap/Dumper.h"
|
||||
#include "zeek/iosource/pcap/Source.h"
|
||||
|
||||
namespace zeek::plugin::detail::Zeek_Pcap
|
||||
{
|
||||
namespace zeek::plugin::detail::Zeek_Pcap {
|
||||
|
||||
class Plugin : public plugin::Plugin
|
||||
{
|
||||
class Plugin : public plugin::Plugin {
|
||||
public:
|
||||
plugin::Configuration Configure() override
|
||||
{
|
||||
AddComponent(new iosource::PktSrcComponent("PcapReader", "pcap",
|
||||
iosource::PktSrcComponent::BOTH,
|
||||
iosource::pcap::PcapSource::Instantiate));
|
||||
AddComponent(new iosource::PktDumperComponent("PcapWriter", "pcap",
|
||||
iosource::pcap::PcapDumper::Instantiate));
|
||||
plugin::Configuration Configure() override {
|
||||
AddComponent(new iosource::PktSrcComponent("PcapReader", "pcap", iosource::PktSrcComponent::BOTH,
|
||||
iosource::pcap::PcapSource::Instantiate));
|
||||
AddComponent(new iosource::PktDumperComponent("PcapWriter", "pcap", iosource::pcap::PcapDumper::Instantiate));
|
||||
|
||||
plugin::Configuration config;
|
||||
config.name = "Zeek::Pcap";
|
||||
config.description = "Packet acquisition via libpcap";
|
||||
return config;
|
||||
}
|
||||
} plugin;
|
||||
plugin::Configuration config;
|
||||
config.name = "Zeek::Pcap";
|
||||
config.description = "Packet acquisition via libpcap";
|
||||
return config;
|
||||
}
|
||||
} plugin;
|
||||
|
||||
} // namespace zeek::plugin::detail::Zeek_Pcap
|
||||
} // namespace zeek::plugin::detail::Zeek_Pcap
|
||||
|
|
|
@ -17,481 +17,423 @@
|
|||
#include "zeek/iosource/Packet.h"
|
||||
#include "zeek/iosource/pcap/pcap.bif.h"
|
||||
|
||||
namespace zeek::iosource::pcap
|
||||
{
|
||||
namespace zeek::iosource::pcap {
|
||||
|
||||
PcapSource::~PcapSource()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
PcapSource::~PcapSource() { Close(); }
|
||||
|
||||
PcapSource::PcapSource(const std::string& path, bool is_live)
|
||||
{
|
||||
props.path = path;
|
||||
props.is_live = is_live;
|
||||
pd = nullptr;
|
||||
}
|
||||
PcapSource::PcapSource(const std::string& path, bool is_live) {
|
||||
props.path = path;
|
||||
props.is_live = is_live;
|
||||
pd = nullptr;
|
||||
}
|
||||
|
||||
void PcapSource::Open()
|
||||
{
|
||||
if ( props.is_live )
|
||||
OpenLive();
|
||||
else
|
||||
OpenOffline();
|
||||
}
|
||||
void PcapSource::Open() {
|
||||
if ( props.is_live )
|
||||
OpenLive();
|
||||
else
|
||||
OpenOffline();
|
||||
}
|
||||
|
||||
void PcapSource::Close()
|
||||
{
|
||||
if ( ! pd )
|
||||
return;
|
||||
void PcapSource::Close() {
|
||||
if ( ! pd )
|
||||
return;
|
||||
|
||||
pcap_close(pd);
|
||||
pd = nullptr;
|
||||
pcap_close(pd);
|
||||
pd = nullptr;
|
||||
|
||||
Closed();
|
||||
Closed();
|
||||
|
||||
if ( Pcap::file_done )
|
||||
event_mgr.Enqueue(Pcap::file_done, make_intrusive<StringVal>(props.path));
|
||||
}
|
||||
if ( Pcap::file_done )
|
||||
event_mgr.Enqueue(Pcap::file_done, make_intrusive<StringVal>(props.path));
|
||||
}
|
||||
|
||||
void PcapSource::OpenLive()
|
||||
{
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
void PcapSource::OpenLive() {
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
// Determine interface if not specified.
|
||||
if ( props.path.empty() )
|
||||
{
|
||||
pcap_if_t* devs;
|
||||
// Determine interface if not specified.
|
||||
if ( props.path.empty() ) {
|
||||
pcap_if_t* devs;
|
||||
|
||||
if ( pcap_findalldevs(&devs, errbuf) < 0 )
|
||||
{
|
||||
Error(util::fmt("pcap_findalldevs: %s", errbuf));
|
||||
return;
|
||||
}
|
||||
if ( pcap_findalldevs(&devs, errbuf) < 0 ) {
|
||||
Error(util::fmt("pcap_findalldevs: %s", errbuf));
|
||||
return;
|
||||
}
|
||||
|
||||
if ( devs )
|
||||
{
|
||||
props.path = devs->name;
|
||||
pcap_freealldevs(devs);
|
||||
if ( devs ) {
|
||||
props.path = devs->name;
|
||||
pcap_freealldevs(devs);
|
||||
|
||||
if ( props.path.empty() )
|
||||
{
|
||||
Error("pcap_findalldevs: empty device name");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Error("pcap_findalldevs: no devices found");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( props.path.empty() ) {
|
||||
Error("pcap_findalldevs: empty device name");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Error("pcap_findalldevs: no devices found");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine network and netmask.
|
||||
uint32_t net;
|
||||
if ( pcap_lookupnet(props.path.c_str(), &net, &props.netmask, errbuf) < 0 )
|
||||
{
|
||||
// ### The lookup can fail if no address is assigned to
|
||||
// the interface; and libpcap doesn't have any useful notion
|
||||
// of error codes, just error std::strings - how bogus - so we
|
||||
// just kludge around the error :-(.
|
||||
// sprintf(errbuf, "pcap_lookupnet %s", errbuf);
|
||||
// return;
|
||||
props.netmask = 0xffffff00;
|
||||
}
|
||||
// Determine network and netmask.
|
||||
uint32_t net;
|
||||
if ( pcap_lookupnet(props.path.c_str(), &net, &props.netmask, errbuf) < 0 ) {
|
||||
// ### The lookup can fail if no address is assigned to
|
||||
// the interface; and libpcap doesn't have any useful notion
|
||||
// of error codes, just error std::strings - how bogus - so we
|
||||
// just kludge around the error :-(.
|
||||
// sprintf(errbuf, "pcap_lookupnet %s", errbuf);
|
||||
// return;
|
||||
props.netmask = 0xffffff00;
|
||||
}
|
||||
|
||||
#ifdef PCAP_NETMASK_UNKNOWN
|
||||
// Defined in libpcap >= 1.1.1
|
||||
if ( props.netmask == PCAP_NETMASK_UNKNOWN )
|
||||
props.netmask = PktSrc::NETMASK_UNKNOWN;
|
||||
// Defined in libpcap >= 1.1.1
|
||||
if ( props.netmask == PCAP_NETMASK_UNKNOWN )
|
||||
props.netmask = PktSrc::NETMASK_UNKNOWN;
|
||||
#endif
|
||||
|
||||
pd = pcap_create(props.path.c_str(), errbuf);
|
||||
pd = pcap_create(props.path.c_str(), errbuf);
|
||||
|
||||
if ( ! pd )
|
||||
{
|
||||
PcapError("pcap_create");
|
||||
return;
|
||||
}
|
||||
if ( ! pd ) {
|
||||
PcapError("pcap_create");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( pcap_set_snaplen(pd, BifConst::Pcap::snaplen) )
|
||||
{
|
||||
PcapError("pcap_set_snaplen");
|
||||
return;
|
||||
}
|
||||
if ( pcap_set_snaplen(pd, BifConst::Pcap::snaplen) ) {
|
||||
PcapError("pcap_set_snaplen");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( pcap_set_promisc(pd, 1) )
|
||||
{
|
||||
PcapError("pcap_set_promisc");
|
||||
return;
|
||||
}
|
||||
if ( pcap_set_promisc(pd, 1) ) {
|
||||
PcapError("pcap_set_promisc");
|
||||
return;
|
||||
}
|
||||
|
||||
// We use the smallest time-out possible to return almost immediately
|
||||
// if no packets are available. (We can't use set_nonblocking() as
|
||||
// it's broken on FreeBSD: even when select() indicates that we can
|
||||
// read something, we may get nothing if the store buffer hasn't
|
||||
// filled up yet.)
|
||||
//
|
||||
// TODO: The comment about FreeBSD is pretty old and may not apply
|
||||
// anymore these days.
|
||||
if ( pcap_set_timeout(pd, 1) )
|
||||
{
|
||||
PcapError("pcap_set_timeout");
|
||||
return;
|
||||
}
|
||||
// We use the smallest time-out possible to return almost immediately
|
||||
// if no packets are available. (We can't use set_nonblocking() as
|
||||
// it's broken on FreeBSD: even when select() indicates that we can
|
||||
// read something, we may get nothing if the store buffer hasn't
|
||||
// filled up yet.)
|
||||
//
|
||||
// TODO: The comment about FreeBSD is pretty old and may not apply
|
||||
// anymore these days.
|
||||
if ( pcap_set_timeout(pd, 1) ) {
|
||||
PcapError("pcap_set_timeout");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( pcap_set_buffer_size(pd, BifConst::Pcap::bufsize * 1024 * 1024) )
|
||||
{
|
||||
PcapError("pcap_set_buffer_size");
|
||||
return;
|
||||
}
|
||||
if ( pcap_set_buffer_size(pd, BifConst::Pcap::bufsize * 1024 * 1024) ) {
|
||||
PcapError("pcap_set_buffer_size");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( pcap_activate(pd) )
|
||||
{
|
||||
PcapError("pcap_activate");
|
||||
return;
|
||||
}
|
||||
if ( pcap_activate(pd) ) {
|
||||
PcapError("pcap_activate");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LINUX
|
||||
if ( pcap_setnonblock(pd, 1, errbuf) < 0 )
|
||||
{
|
||||
PcapError("pcap_setnonblock");
|
||||
return;
|
||||
}
|
||||
if ( pcap_setnonblock(pd, 1, errbuf) < 0 ) {
|
||||
PcapError("pcap_setnonblock");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PCAP_INT_H
|
||||
Info(util::fmt("pcap bufsize = %d\n", ((struct pcap*)pd)->bufsize));
|
||||
Info(util::fmt("pcap bufsize = %d\n", ((struct pcap*)pd)->bufsize));
|
||||
#endif
|
||||
|
||||
#ifndef _MSC_VER
|
||||
props.selectable_fd = pcap_get_selectable_fd(pd);
|
||||
props.selectable_fd = pcap_get_selectable_fd(pd);
|
||||
#endif
|
||||
|
||||
props.link_type = pcap_datalink(pd);
|
||||
props.is_live = true;
|
||||
props.link_type = pcap_datalink(pd);
|
||||
props.is_live = true;
|
||||
|
||||
Opened(props);
|
||||
}
|
||||
Opened(props);
|
||||
}
|
||||
|
||||
void PcapSource::OpenOffline()
|
||||
{
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
void PcapSource::OpenOffline() {
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
FILE* f = nullptr;
|
||||
if ( props.path == "-" )
|
||||
{
|
||||
f = stdin;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( f = fopen(props.path.c_str(), "rb"); ! f )
|
||||
{
|
||||
Error(util::fmt("unable to open %s: %s", props.path.c_str(), strerror(errno)));
|
||||
return;
|
||||
}
|
||||
FILE* f = nullptr;
|
||||
if ( props.path == "-" ) {
|
||||
f = stdin;
|
||||
}
|
||||
else {
|
||||
if ( f = fopen(props.path.c_str(), "rb"); ! f ) {
|
||||
Error(util::fmt("unable to open %s: %s", props.path.c_str(), strerror(errno)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup file IO buffering with a bufsize_offline_bytes sized
|
||||
// buffer if set, otherwise use what fopen() took as the default.
|
||||
if ( BifConst::Pcap::bufsize_offline_bytes != 0 )
|
||||
{
|
||||
iobuf.resize(BifConst::Pcap::bufsize_offline_bytes);
|
||||
if ( util::detail::setvbuf(f, iobuf.data(), _IOFBF, iobuf.size()) != 0 )
|
||||
{
|
||||
Error(util::fmt("unable to setvbuf %s: %s", props.path.c_str(), strerror(errno)));
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Setup file IO buffering with a bufsize_offline_bytes sized
|
||||
// buffer if set, otherwise use what fopen() took as the default.
|
||||
if ( BifConst::Pcap::bufsize_offline_bytes != 0 ) {
|
||||
iobuf.resize(BifConst::Pcap::bufsize_offline_bytes);
|
||||
if ( util::detail::setvbuf(f, iobuf.data(), _IOFBF, iobuf.size()) != 0 ) {
|
||||
Error(util::fmt("unable to setvbuf %s: %s", props.path.c_str(), strerror(errno)));
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pcap_fopen_offline() takes ownership of f on success and
|
||||
// pcap_close() elsewhere should close it, too.
|
||||
pd = pcap_fopen_offline(f, errbuf);
|
||||
// pcap_fopen_offline() takes ownership of f on success and
|
||||
// pcap_close() elsewhere should close it, too.
|
||||
pd = pcap_fopen_offline(f, errbuf);
|
||||
|
||||
if ( ! pd )
|
||||
{
|
||||
if ( f != stdin )
|
||||
fclose(f);
|
||||
if ( ! pd ) {
|
||||
if ( f != stdin )
|
||||
fclose(f);
|
||||
|
||||
Error(errbuf);
|
||||
return;
|
||||
}
|
||||
Error(errbuf);
|
||||
return;
|
||||
}
|
||||
|
||||
// We don't register the file descriptor if we're in offline mode,
|
||||
// because libpcap's file descriptor for trace files isn't a reliable
|
||||
// way to know whether we actually have data to read.
|
||||
// See https://github.com/the-tcpdump-group/libpcap/issues/870
|
||||
props.selectable_fd = -1;
|
||||
// We don't register the file descriptor if we're in offline mode,
|
||||
// because libpcap's file descriptor for trace files isn't a reliable
|
||||
// way to know whether we actually have data to read.
|
||||
// See https://github.com/the-tcpdump-group/libpcap/issues/870
|
||||
props.selectable_fd = -1;
|
||||
|
||||
props.link_type = pcap_datalink(pd);
|
||||
props.is_live = false;
|
||||
props.link_type = pcap_datalink(pd);
|
||||
props.is_live = false;
|
||||
|
||||
Opened(props);
|
||||
}
|
||||
Opened(props);
|
||||
}
|
||||
|
||||
bool PcapSource::ExtractNextPacket(Packet* pkt)
|
||||
{
|
||||
if ( ! pd )
|
||||
return false;
|
||||
bool PcapSource::ExtractNextPacket(Packet* pkt) {
|
||||
if ( ! pd )
|
||||
return false;
|
||||
|
||||
const u_char* data;
|
||||
pcap_pkthdr* header;
|
||||
const u_char* data;
|
||||
pcap_pkthdr* header;
|
||||
|
||||
int res = pcap_next_ex(pd, &header, &data);
|
||||
int res = pcap_next_ex(pd, &header, &data);
|
||||
|
||||
switch ( res )
|
||||
{
|
||||
case PCAP_ERROR_BREAK: // -2
|
||||
// Exhausted pcap file, no more packets to read.
|
||||
assert(! props.is_live);
|
||||
Close();
|
||||
return false;
|
||||
case PCAP_ERROR: // -1
|
||||
// Error occurred while reading the packet.
|
||||
if ( props.is_live )
|
||||
reporter->Error("failed to read a packet from %s: %s", props.path.data(),
|
||||
pcap_geterr(pd));
|
||||
else
|
||||
reporter->FatalError("failed to read a packet from %s: %s", props.path.data(),
|
||||
pcap_geterr(pd));
|
||||
return false;
|
||||
case 0:
|
||||
// Read from live interface timed out (ok).
|
||||
return false;
|
||||
case 1:
|
||||
// Read a packet without problem.
|
||||
// Although, some libpcaps may claim to have read a packet, but either did
|
||||
// not really read a packet or at least provide no way to access its
|
||||
// contents, so the following check for null-data helps handle those cases.
|
||||
if ( ! data )
|
||||
{
|
||||
reporter->Weird("pcap_null_data_packet");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
reporter->InternalError("unhandled pcap_next_ex return value: %d", res);
|
||||
return false;
|
||||
}
|
||||
switch ( res ) {
|
||||
case PCAP_ERROR_BREAK: // -2
|
||||
// Exhausted pcap file, no more packets to read.
|
||||
assert(! props.is_live);
|
||||
Close();
|
||||
return false;
|
||||
case PCAP_ERROR: // -1
|
||||
// Error occurred while reading the packet.
|
||||
if ( props.is_live )
|
||||
reporter->Error("failed to read a packet from %s: %s", props.path.data(), pcap_geterr(pd));
|
||||
else
|
||||
reporter->FatalError("failed to read a packet from %s: %s", props.path.data(), pcap_geterr(pd));
|
||||
return false;
|
||||
case 0:
|
||||
// Read from live interface timed out (ok).
|
||||
return false;
|
||||
case 1:
|
||||
// Read a packet without problem.
|
||||
// Although, some libpcaps may claim to have read a packet, but either did
|
||||
// not really read a packet or at least provide no way to access its
|
||||
// contents, so the following check for null-data helps handle those cases.
|
||||
if ( ! data ) {
|
||||
reporter->Weird("pcap_null_data_packet");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default: reporter->InternalError("unhandled pcap_next_ex return value: %d", res); return false;
|
||||
}
|
||||
|
||||
pkt->Init(props.link_type, &header->ts, header->caplen, header->len, data);
|
||||
pkt->Init(props.link_type, &header->ts, header->caplen, header->len, data);
|
||||
|
||||
if ( header->len == 0 || header->caplen == 0 )
|
||||
{
|
||||
Weird("empty_pcap_header", pkt);
|
||||
return false;
|
||||
}
|
||||
if ( header->len == 0 || header->caplen == 0 ) {
|
||||
Weird("empty_pcap_header", pkt);
|
||||
return false;
|
||||
}
|
||||
|
||||
++stats.received;
|
||||
stats.bytes_received += header->len;
|
||||
++stats.received;
|
||||
stats.bytes_received += header->len;
|
||||
|
||||
// Some versions of libpcap (myricom) are somewhat broken and will return a duplicate
|
||||
// packet if there are no more packets available. Namely, it returns the exact same
|
||||
// packet structure (including the header) out of the library without reinitializing
|
||||
// any of the values. If we set the header lengths to zero here, we can keep from
|
||||
// processing it a second time.
|
||||
header->len = 0;
|
||||
header->caplen = 0;
|
||||
// Some versions of libpcap (myricom) are somewhat broken and will return a duplicate
|
||||
// packet if there are no more packets available. Namely, it returns the exact same
|
||||
// packet structure (including the header) out of the library without reinitializing
|
||||
// any of the values. If we set the header lengths to zero here, we can keep from
|
||||
// processing it a second time.
|
||||
header->len = 0;
|
||||
header->caplen = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PcapSource::DoneWithPacket()
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
void PcapSource::DoneWithPacket() {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
detail::BPF_Program* PcapSource::CompileFilter(const std::string& filter)
|
||||
{
|
||||
auto code = std::make_unique<detail::BPF_Program>();
|
||||
detail::BPF_Program* PcapSource::CompileFilter(const std::string& filter) {
|
||||
auto code = std::make_unique<detail::BPF_Program>();
|
||||
|
||||
if ( ! code->Compile(pd, filter.c_str(), Netmask()) )
|
||||
{
|
||||
std::string msg = util::fmt("cannot compile BPF filter \"%s\"", filter.c_str());
|
||||
if ( ! code->Compile(pd, filter.c_str(), Netmask()) ) {
|
||||
std::string msg = util::fmt("cannot compile BPF filter \"%s\"", filter.c_str());
|
||||
|
||||
std::string state_msg = code->GetStateMessage();
|
||||
if ( ! state_msg.empty() )
|
||||
msg += ": " + state_msg;
|
||||
std::string state_msg = code->GetStateMessage();
|
||||
if ( ! state_msg.empty() )
|
||||
msg += ": " + state_msg;
|
||||
|
||||
Error(msg);
|
||||
}
|
||||
Error(msg);
|
||||
}
|
||||
|
||||
return code.release();
|
||||
}
|
||||
return code.release();
|
||||
}
|
||||
|
||||
bool PcapSource::SetFilter(int index)
|
||||
{
|
||||
if ( ! pd )
|
||||
return true; // Prevent error message
|
||||
bool PcapSource::SetFilter(int index) {
|
||||
if ( ! pd )
|
||||
return true; // Prevent error message
|
||||
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
iosource::detail::BPF_Program* code = GetBPFFilter(index);
|
||||
iosource::detail::BPF_Program* code = GetBPFFilter(index);
|
||||
|
||||
if ( ! code )
|
||||
{
|
||||
snprintf(errbuf, sizeof(errbuf), "No precompiled pcap filter for index %d", index);
|
||||
Error(errbuf);
|
||||
return false;
|
||||
}
|
||||
if ( ! code ) {
|
||||
snprintf(errbuf, sizeof(errbuf), "No precompiled pcap filter for index %d", index);
|
||||
Error(errbuf);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( LinkType() == DLT_NFLOG )
|
||||
{
|
||||
// No-op, NFLOG does not support BPF filters.
|
||||
// Raising a warning might be good, but it would also be noisy
|
||||
// since the default scripts will always attempt to compile
|
||||
// and install a default filter
|
||||
}
|
||||
else if ( auto program = code->GetProgram() )
|
||||
{
|
||||
if ( pcap_setfilter(pd, program) < 0 )
|
||||
{
|
||||
PcapError();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ( code->GetState() != FilterState::OK )
|
||||
return false;
|
||||
if ( LinkType() == DLT_NFLOG ) {
|
||||
// No-op, NFLOG does not support BPF filters.
|
||||
// Raising a warning might be good, but it would also be noisy
|
||||
// since the default scripts will always attempt to compile
|
||||
// and install a default filter
|
||||
}
|
||||
else if ( auto program = code->GetProgram() ) {
|
||||
if ( pcap_setfilter(pd, program) < 0 ) {
|
||||
PcapError();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ( code->GetState() != FilterState::OK )
|
||||
return false;
|
||||
|
||||
#ifndef HAVE_LINUX
|
||||
// Linux doesn't clear counters when resetting filter.
|
||||
stats.received = stats.dropped = stats.link = stats.bytes_received = 0;
|
||||
// Linux doesn't clear counters when resetting filter.
|
||||
stats.received = stats.dropped = stats.link = stats.bytes_received = 0;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Given two pcap_stat structures, compute the difference of linked and dropped
|
||||
// and add it to the given Stats object.
|
||||
static void update_pktsrc_stats(PktSrc::Stats* stats, const struct pcap_stat* now,
|
||||
const struct pcap_stat* prev)
|
||||
{
|
||||
decltype(now->ps_drop) ps_drop_diff = 0;
|
||||
decltype(now->ps_recv) ps_recv_diff = 0;
|
||||
static void update_pktsrc_stats(PktSrc::Stats* stats, const struct pcap_stat* now, const struct pcap_stat* prev) {
|
||||
decltype(now->ps_drop) ps_drop_diff = 0;
|
||||
decltype(now->ps_recv) ps_recv_diff = 0;
|
||||
|
||||
// This is subtraction of unsigned ints: It's not undefined
|
||||
// and results in modulo arithmetic.
|
||||
ps_recv_diff = now->ps_recv - prev->ps_recv;
|
||||
ps_drop_diff = now->ps_drop - prev->ps_drop;
|
||||
// This is subtraction of unsigned ints: It's not undefined
|
||||
// and results in modulo arithmetic.
|
||||
ps_recv_diff = now->ps_recv - prev->ps_recv;
|
||||
ps_drop_diff = now->ps_drop - prev->ps_drop;
|
||||
|
||||
stats->link += ps_recv_diff;
|
||||
stats->dropped += ps_drop_diff;
|
||||
}
|
||||
stats->link += ps_recv_diff;
|
||||
stats->dropped += ps_drop_diff;
|
||||
}
|
||||
|
||||
void PcapSource::Statistics(Stats* s)
|
||||
{
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
void PcapSource::Statistics(Stats* s) {
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
|
||||
if ( ! (props.is_live && pd) )
|
||||
s->received = s->dropped = s->link = s->bytes_received = 0;
|
||||
if ( ! (props.is_live && pd) )
|
||||
s->received = s->dropped = s->link = s->bytes_received = 0;
|
||||
|
||||
else
|
||||
{
|
||||
struct pcap_stat pstat;
|
||||
if ( pcap_stats(pd, &pstat) < 0 )
|
||||
{
|
||||
PcapError();
|
||||
s->received = s->dropped = s->link = s->bytes_received = 0;
|
||||
}
|
||||
else {
|
||||
struct pcap_stat pstat;
|
||||
if ( pcap_stats(pd, &pstat) < 0 ) {
|
||||
PcapError();
|
||||
s->received = s->dropped = s->link = s->bytes_received = 0;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
update_pktsrc_stats(&stats, &pstat, &prev_pstat);
|
||||
prev_pstat = pstat;
|
||||
}
|
||||
}
|
||||
else {
|
||||
update_pktsrc_stats(&stats, &pstat, &prev_pstat);
|
||||
prev_pstat = pstat;
|
||||
}
|
||||
}
|
||||
|
||||
s->link = stats.link;
|
||||
s->dropped = stats.dropped;
|
||||
s->received = stats.received;
|
||||
s->bytes_received = stats.bytes_received;
|
||||
s->link = stats.link;
|
||||
s->dropped = stats.dropped;
|
||||
s->received = stats.received;
|
||||
s->bytes_received = stats.bytes_received;
|
||||
|
||||
if ( ! props.is_live )
|
||||
s->dropped = 0;
|
||||
}
|
||||
if ( ! props.is_live )
|
||||
s->dropped = 0;
|
||||
}
|
||||
|
||||
void PcapSource::PcapError(const char* where)
|
||||
{
|
||||
std::string location;
|
||||
void PcapSource::PcapError(const char* where) {
|
||||
std::string location;
|
||||
|
||||
if ( where )
|
||||
location = util::fmt(" (%s)", where);
|
||||
if ( where )
|
||||
location = util::fmt(" (%s)", where);
|
||||
|
||||
if ( pd )
|
||||
Error(util::fmt("pcap_error: %s%s", pcap_geterr(pd), location.c_str()));
|
||||
else
|
||||
Error(util::fmt("pcap_error: not open%s", location.c_str()));
|
||||
if ( pd )
|
||||
Error(util::fmt("pcap_error: %s%s", pcap_geterr(pd), location.c_str()));
|
||||
else
|
||||
Error(util::fmt("pcap_error: not open%s", location.c_str()));
|
||||
|
||||
Close();
|
||||
}
|
||||
Close();
|
||||
}
|
||||
|
||||
iosource::PktSrc* PcapSource::Instantiate(const std::string& path, bool is_live)
|
||||
{
|
||||
return new PcapSource(path, is_live);
|
||||
}
|
||||
iosource::PktSrc* PcapSource::Instantiate(const std::string& path, bool is_live) {
|
||||
return new PcapSource(path, is_live);
|
||||
}
|
||||
|
||||
TEST_CASE("pcap source update_pktsrc_stats")
|
||||
{
|
||||
PktSrc::Stats stats;
|
||||
struct pcap_stat now = {0};
|
||||
struct pcap_stat prev = {0};
|
||||
TEST_CASE("pcap source update_pktsrc_stats") {
|
||||
PktSrc::Stats stats;
|
||||
struct pcap_stat now = {0};
|
||||
struct pcap_stat prev = {0};
|
||||
|
||||
SUBCASE("all zero")
|
||||
{
|
||||
update_pktsrc_stats(&stats, &now, &prev);
|
||||
CHECK(stats.link == 0);
|
||||
CHECK(stats.dropped == 0);
|
||||
}
|
||||
SUBCASE("all zero") {
|
||||
update_pktsrc_stats(&stats, &now, &prev);
|
||||
CHECK(stats.link == 0);
|
||||
CHECK(stats.dropped == 0);
|
||||
}
|
||||
|
||||
SUBCASE("no overflow")
|
||||
{
|
||||
now.ps_recv = 7;
|
||||
now.ps_drop = 3;
|
||||
update_pktsrc_stats(&stats, &now, &prev);
|
||||
CHECK(stats.link == 7);
|
||||
CHECK(stats.dropped == 3);
|
||||
}
|
||||
SUBCASE("no overflow") {
|
||||
now.ps_recv = 7;
|
||||
now.ps_drop = 3;
|
||||
update_pktsrc_stats(&stats, &now, &prev);
|
||||
CHECK(stats.link == 7);
|
||||
CHECK(stats.dropped == 3);
|
||||
}
|
||||
|
||||
SUBCASE("no overflow prev")
|
||||
{
|
||||
stats.link = 2;
|
||||
stats.dropped = 1;
|
||||
prev.ps_recv = 2;
|
||||
prev.ps_drop = 1;
|
||||
now.ps_recv = 7;
|
||||
now.ps_drop = 3;
|
||||
SUBCASE("no overflow prev") {
|
||||
stats.link = 2;
|
||||
stats.dropped = 1;
|
||||
prev.ps_recv = 2;
|
||||
prev.ps_drop = 1;
|
||||
now.ps_recv = 7;
|
||||
now.ps_drop = 3;
|
||||
|
||||
update_pktsrc_stats(&stats, &now, &prev);
|
||||
CHECK(stats.link == 7);
|
||||
CHECK(stats.dropped == 3);
|
||||
}
|
||||
update_pktsrc_stats(&stats, &now, &prev);
|
||||
CHECK(stats.link == 7);
|
||||
CHECK(stats.dropped == 3);
|
||||
}
|
||||
|
||||
SUBCASE("overflow")
|
||||
{
|
||||
prev.ps_recv = 4294967295;
|
||||
prev.ps_drop = 4294967294;
|
||||
now.ps_recv = 0;
|
||||
now.ps_drop = 1;
|
||||
SUBCASE("overflow") {
|
||||
prev.ps_recv = 4294967295;
|
||||
prev.ps_drop = 4294967294;
|
||||
now.ps_recv = 0;
|
||||
now.ps_drop = 1;
|
||||
|
||||
update_pktsrc_stats(&stats, &now, &prev);
|
||||
CHECK(stats.link == 1);
|
||||
CHECK(stats.dropped == 3);
|
||||
}
|
||||
update_pktsrc_stats(&stats, &now, &prev);
|
||||
CHECK(stats.link == 1);
|
||||
CHECK(stats.dropped == 3);
|
||||
}
|
||||
|
||||
SUBCASE("overflow 2")
|
||||
{
|
||||
stats.link = 4294967295;
|
||||
stats.dropped = 4294967294;
|
||||
prev.ps_recv = 4294967295;
|
||||
prev.ps_drop = 4294967294;
|
||||
now.ps_recv = 10;
|
||||
now.ps_drop = 3;
|
||||
SUBCASE("overflow 2") {
|
||||
stats.link = 4294967295;
|
||||
stats.dropped = 4294967294;
|
||||
prev.ps_recv = 4294967295;
|
||||
prev.ps_drop = 4294967294;
|
||||
now.ps_recv = 10;
|
||||
now.ps_drop = 3;
|
||||
|
||||
update_pktsrc_stats(&stats, &now, &prev);
|
||||
CHECK(stats.link == 4294967306); // 2**32 - 1 + 11
|
||||
CHECK(stats.dropped == 4294967299); // 2**32 - 2 + 5
|
||||
}
|
||||
}
|
||||
update_pktsrc_stats(&stats, &now, &prev);
|
||||
CHECK(stats.link == 4294967306); // 2**32 - 1 + 11
|
||||
CHECK(stats.dropped == 4294967299); // 2**32 - 2 + 5
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace zeek::iosource::pcap
|
||||
} // namespace zeek::iosource::pcap
|
||||
|
|
|
@ -6,48 +6,45 @@
|
|||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
extern "C" {
|
||||
#include <pcap.h>
|
||||
}
|
||||
}
|
||||
|
||||
#include "zeek/iosource/PktSrc.h"
|
||||
|
||||
namespace zeek::iosource::pcap
|
||||
{
|
||||
namespace zeek::iosource::pcap {
|
||||
|
||||
class PcapSource : public PktSrc
|
||||
{
|
||||
class PcapSource : public PktSrc {
|
||||
public:
|
||||
PcapSource(const std::string& path, bool is_live);
|
||||
~PcapSource() override;
|
||||
PcapSource(const std::string& path, bool is_live);
|
||||
~PcapSource() override;
|
||||
|
||||
static PktSrc* Instantiate(const std::string& path, bool is_live);
|
||||
static PktSrc* Instantiate(const std::string& path, bool is_live);
|
||||
|
||||
protected:
|
||||
// PktSrc interface.
|
||||
void Open() override;
|
||||
void Close() override;
|
||||
bool ExtractNextPacket(Packet* pkt) override;
|
||||
void DoneWithPacket() override;
|
||||
bool SetFilter(int index) override;
|
||||
void Statistics(Stats* stats) override;
|
||||
// PktSrc interface.
|
||||
void Open() override;
|
||||
void Close() override;
|
||||
bool ExtractNextPacket(Packet* pkt) override;
|
||||
void DoneWithPacket() override;
|
||||
bool SetFilter(int index) override;
|
||||
void Statistics(Stats* stats) override;
|
||||
|
||||
detail::BPF_Program* CompileFilter(const std::string& filter) override;
|
||||
detail::BPF_Program* CompileFilter(const std::string& filter) override;
|
||||
|
||||
private:
|
||||
void OpenLive();
|
||||
void OpenOffline();
|
||||
void PcapError(const char* where = nullptr);
|
||||
void OpenLive();
|
||||
void OpenOffline();
|
||||
void PcapError(const char* where = nullptr);
|
||||
|
||||
Properties props;
|
||||
Stats stats;
|
||||
Properties props;
|
||||
Stats stats;
|
||||
|
||||
pcap_t* pd;
|
||||
struct pcap_stat prev_pstat = {0};
|
||||
pcap_t* pd;
|
||||
struct pcap_stat prev_pstat = {0};
|
||||
|
||||
// Buffer provided to setvbuf() when reading from a PCAP file.
|
||||
std::vector<char> iobuf;
|
||||
};
|
||||
// Buffer provided to setvbuf() when reading from a PCAP file.
|
||||
std::vector<char> iobuf;
|
||||
};
|
||||
|
||||
} // namespace zeek::iosource::pcap
|
||||
} // namespace zeek::iosource::pcap
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue