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:
Benjamin Bannier 2023-10-10 21:13:34 +02:00
parent 7b8e7ed72c
commit f5a76c1aed
786 changed files with 131714 additions and 153609 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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(&current_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(&current_packet) )
{
had_packet = true;
if ( current_packet.time < 0 )
{
Weird("negative_packet_timestamp", &current_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 = &current_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(&current_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(&current_packet) ) {
had_packet = true;
if ( current_packet.time < 0 ) {
Weird("negative_packet_timestamp", &current_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 = &current_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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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