Moving some of the BPF filtering code into base class.

This will allow packet sources that don't support BPF natively to
emulate the filtering via libpcap.
This commit is contained in:
Robin Sommer 2014-08-22 17:27:20 -07:00
parent 0186061aa8
commit ce9f16490c
8 changed files with 110 additions and 85 deletions

View file

@ -7,6 +7,7 @@ include_directories(BEFORE
) )
set(iosource_SRCS set(iosource_SRCS
BPF_Program.cc
Component.cc Component.cc
Manager.cc Manager.cc
PktDumper.cc PktDumper.cc

View file

@ -25,6 +25,10 @@ PktSrc::PktSrc()
PktSrc::~PktSrc() PktSrc::~PktSrc()
{ {
BPF_Program* code;
IterCookie* cookie = filters.InitForIteration();
while ( (code = filters.NextEntry(cookie)) )
delete code;
} }
const std::string& PktSrc::Path() const const std::string& PktSrc::Path() const
@ -43,6 +47,11 @@ int PktSrc::LinkType() const
return IsOpen() ? props.link_type : -1; return IsOpen() ? props.link_type : -1;
} }
uint32 PktSrc::Netmask() const
{
return IsOpen() ? props.netmask : PCAP_NETMASK_UNKNOWN;
}
int PktSrc::HdrSize() const int PktSrc::HdrSize() const
{ {
return IsOpen() ? props.hdr_size : -1; return IsOpen() ? props.hdr_size : -1;
@ -77,6 +86,12 @@ void PktSrc::Opened(const Properties& arg_props)
props = arg_props; props = arg_props;
SetClosed(false); SetClosed(false);
if ( ! PrecompileFilter(0, props.filter) || ! SetFilter(0) )
{
Close();
return;
}
DBG_LOG(DBG_PKTIO, "Opened source %s", props.path.c_str()); DBG_LOG(DBG_PKTIO, "Opened source %s", props.path.c_str());
} }
@ -409,12 +424,50 @@ int PktSrc::ExtractNextPacketInternal()
return 0; return 0;
} }
int PktSrc::PrecompileFilter(int index, const std::string& filter) int PktSrc::PrecompileBPFFilter(int index, const std::string& filter)
{ {
char errbuf[PCAP_ERRBUF_SIZE];
// Compile filter.
BPF_Program* code = new BPF_Program();
if ( ! code->Compile(SnapLen(), LinkType(), filter.c_str(), Netmask(), errbuf, sizeof(errbuf)) )
{
Error(fmt("cannot compile BPF filter \"%s\": %s", filter.c_str(), errbuf));
Close();
delete code;
return 0;
}
// Store it in hash.
HashKey* hash = new HashKey(HashKey(bro_int_t(index)));
BPF_Program* oldcode = filters.Lookup(hash);
if ( oldcode )
delete oldcode;
filters.Insert(hash, code);
delete hash;
return 1; return 1;
} }
int PktSrc::SetFilter(int index) BPF_Program* PktSrc::GetBPFFilter(int index)
{ {
return 1; HashKey* hash = new HashKey(HashKey(bro_int_t(index)));
BPF_Program* code = filters.Lookup(hash);
delete hash;
return code;
}
int PktSrc::ApplyBPFFilter(int index, const struct pcap_pkthdr *hdr, const u_char *pkt)
{
BPF_Program* code = GetBPFFilter(index);
if ( ! code )
{
Error(fmt("BPF filter %d not compiled", index));
Close();
}
return pcap_offline_filter(code->GetProgram(), hdr, pkt);
} }

View file

@ -8,6 +8,10 @@ extern "C" {
} }
#include "IOSource.h" #include "IOSource.h"
#include "BPF_Program.h"
#include "Dict.h"
declare(PDict,BPF_Program);
namespace iosource { namespace iosource {
@ -29,6 +33,7 @@ public:
const std::string& Filter() const; const std::string& Filter() const;
bool IsLive() const; bool IsLive() const;
int LinkType() const; int LinkType() const;
uint32 Netmask() const;
const char* ErrorMsg() const; const char* ErrorMsg() const;
int HdrSize() const; int HdrSize() const;
int SnapLen() const; int SnapLen() const;
@ -41,7 +46,22 @@ public:
// going to be continued. // going to be continued.
void ContinueAfterSuspend(); void ContinueAfterSuspend();
virtual void Statistics(Stats* stats) = 0; // Precompiles a BPF filter and associates the given index with it.
// Returns true on success, 0 if a problem occurred. The compiled
// filter will be then available via GetBPFFilter*(.
int PrecompileBPFFilter(int index, const std::string& filter);
// Returns the BPF filter with the given index, as compiled by
// PrecompileBPFFilter(), or null if none has been (successfully)
// compiled.
BPF_Program* GetBPFFilter(int index);
// Applies a precompiled BPF filter to a packet, returning true if it
// maches. This will close the source with an error message if no
// filter with that index has been compiled.
int ApplyBPFFilter(int index, const struct pcap_pkthdr *hdr, const u_char *pkt);
// PacketSource interace for derived classes to override.
// Returns the packet last processed; false if there is no // Returns the packet last processed; false if there is no
// current packet available. // current packet available.
@ -50,12 +70,15 @@ public:
// Precompiles a filter and associates the given index with it. // Precompiles a filter and associates the given index with it.
// Returns true on success, 0 if a problem occurred or filtering is // Returns true on success, 0 if a problem occurred or filtering is
// not supported. // not supported.
virtual int PrecompileFilter(int index, const std::string& filter); virtual int PrecompileFilter(int index, const std::string& filter) = 0;
// Activates the filter with the given index. Returns true on // Activates the filter with the given index. Returns true on
// success, 0 if a problem occurred or the filtering is not // success, 0 if a problem occurred or the filtering is not
// supported. // supported.
virtual int SetFilter(int index); virtual int SetFilter(int index) = 0;
// Returns current statistics about the source.
virtual void Statistics(Stats* stats) = 0;
static int GetLinkHeaderSize(int link_type); static int GetLinkHeaderSize(int link_type);
@ -68,7 +91,13 @@ protected:
int selectable_fd; int selectable_fd;
int link_type; int link_type;
int hdr_size; int hdr_size;
uint32 netmask;
bool is_live; bool is_live;
Properties()
{
netmask = PCAP_NETMASK_UNKNOWN;
}
}; };
struct Packet { struct Packet {
@ -113,6 +142,9 @@ private:
bool have_packet; bool have_packet;
Packet current_packet; Packet current_packet;
// For BPF filtering support.
PDict(BPF_Program) filters;
// Only set in pseudo-realtime mode. // Only set in pseudo-realtime mode.
double first_timestamp; double first_timestamp;
double first_wallclock; double first_wallclock;

View file

@ -4,5 +4,5 @@ include(BroPlugin)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
bro_plugin_begin(Bro Pcap) bro_plugin_begin(Bro Pcap)
bro_plugin_cc(Source.cc Dumper.cc BPF_Program.cc Plugin.cc) bro_plugin_cc(Source.cc Dumper.cc Plugin.cc)
bro_plugin_end() bro_plugin_end()

View file

@ -37,13 +37,6 @@ void PcapSource::Close()
if ( ! pd ) if ( ! pd )
return; return;
BPF_Program* code;
IterCookie* cookie = filters.InitForIteration();
while ( (code = filters.NextEntry(cookie)) )
delete code;
filters.Clear();
pcap_close(pd); pcap_close(pd);
pd = 0; pd = 0;
last_data = 0; last_data = 0;
@ -56,10 +49,6 @@ void PcapSource::OpenLive()
char errbuf[PCAP_ERRBUF_SIZE]; char errbuf[PCAP_ERRBUF_SIZE];
char tmp_errbuf[PCAP_ERRBUF_SIZE]; char tmp_errbuf[PCAP_ERRBUF_SIZE];
#if 0
filter_type = ft;
#endif
// Determine interface if not specified. // Determine interface if not specified.
if ( props.path.empty() ) if ( props.path.empty() )
props.path = pcap_lookupdev(tmp_errbuf); props.path = pcap_lookupdev(tmp_errbuf);
@ -74,7 +63,7 @@ void PcapSource::OpenLive()
// Determine network and netmask. // Determine network and netmask.
uint32 net; uint32 net;
if ( pcap_lookupnet(props.path.c_str(), &net, &netmask, tmp_errbuf) < 0 ) if ( pcap_lookupnet(props.path.c_str(), &net, &props.netmask, tmp_errbuf) < 0 )
{ {
// ### The lookup can fail if no address is assigned to // ### The lookup can fail if no address is assigned to
// the interface; and libpcap doesn't have any useful notion // the interface; and libpcap doesn't have any useful notion
@ -82,7 +71,7 @@ void PcapSource::OpenLive()
// just kludge around the error :-(. // just kludge around the error :-(.
// sprintf(errbuf, "pcap_lookupnet %s", tmp_errbuf); // sprintf(errbuf, "pcap_lookupnet %s", tmp_errbuf);
// return; // return;
netmask = 0xffffff00; props.netmask = 0xffffff00;
} }
// We use the smallest time-out possible to return almost immediately if // We use the smallest time-out possible to return almost immediately if
@ -113,31 +102,22 @@ void PcapSource::OpenLive()
props.selectable_fd = pcap_fileno(pd); props.selectable_fd = pcap_fileno(pd);
if ( PrecompileFilter(0, props.filter) && SetFilter(0) )
{
SetHdrSize(); SetHdrSize();
if ( ! pd ) if ( ! pd )
// Was closed, couldn't get header size. // Was closed, couldn't get header size.
return; return;
Info(fmt("listening on %s, capture length %d bytes\n", props.path.c_str(), SnapLen()));
}
else
Close();
props.is_live = true; props.is_live = true;
Opened(props); Opened(props);
Info(fmt("listening on %s, capture length %d bytes\n", props.path.c_str(), SnapLen()));
} }
void PcapSource::OpenOffline() void PcapSource::OpenOffline()
{ {
char errbuf[PCAP_ERRBUF_SIZE]; char errbuf[PCAP_ERRBUF_SIZE];
#if 0
filter_type = ft;
#endif
pd = pcap_open_offline(props.path.c_str(), errbuf); pd = pcap_open_offline(props.path.c_str(), errbuf);
if ( ! pd ) if ( ! pd )
@ -146,25 +126,16 @@ void PcapSource::OpenOffline()
return; return;
} }
if ( PrecompileFilter(0, props.filter) && SetFilter(0) )
{
SetHdrSize(); SetHdrSize();
if ( ! pd ) if ( ! pd )
// Was closed, unknown link layer type. // Was closed, unknown link layer type.
return; return;
// We don't put file sources into non-blocking mode as
// otherwise we would not be able to identify the EOF.
props.selectable_fd = fileno(pcap_file(pd)); props.selectable_fd = fileno(pcap_file(pd));
if ( props.selectable_fd < 0 ) if ( props.selectable_fd < 0 )
InternalError("OS does not support selectable pcap fd"); InternalError("OS does not support selectable pcap fd");
}
else
Close();
props.is_live = false; props.is_live = false;
Opened(props); Opened(props);
@ -211,31 +182,7 @@ void PcapSource::DoneWithPacket(Packet* pkt)
int PcapSource::PrecompileFilter(int index, const std::string& filter) int PcapSource::PrecompileFilter(int index, const std::string& filter)
{ {
if ( ! pd ) return PktSrc::PrecompileBPFFilter(index, filter).
return 1; // Prevent error message.
char errbuf[PCAP_ERRBUF_SIZE];
// Compile filter.
BPF_Program* code = new BPF_Program();
if ( ! code->Compile(pd, filter.c_str(), netmask, errbuf, sizeof(errbuf)) )
{
PcapError();
delete code;
return 0;
}
// Store it in hash.
HashKey* hash = new HashKey(HashKey(bro_int_t(index)));
BPF_Program* oldcode = filters.Lookup(hash);
if ( oldcode )
delete oldcode;
filters.Insert(hash, code);
delete hash;
return 1;
} }
int PcapSource::SetFilter(int index) int PcapSource::SetFilter(int index)
@ -245,9 +192,7 @@ int PcapSource::SetFilter(int index)
char errbuf[PCAP_ERRBUF_SIZE]; char errbuf[PCAP_ERRBUF_SIZE];
HashKey* hash = new HashKey(HashKey(bro_int_t(index))); BPF_Program* code = GetFilter(index);
BPF_Program* code = filters.Lookup(hash);
delete hash;
if ( ! code ) if ( ! code )
{ {

View file

@ -4,10 +4,6 @@
#define IOSOURCE_PKTSRC_PCAP_SOURCE_H #define IOSOURCE_PKTSRC_PCAP_SOURCE_H
#include "../PktSrc.h" #include "../PktSrc.h"
#include "BPF_Program.h"
#include "Dict.h"
declare(PDict,BPF_Program);
namespace iosource { namespace iosource {
namespace pktsrc { namespace pktsrc {
@ -42,8 +38,6 @@ private:
Stats stats; Stats stats;
pcap_t *pd; pcap_t *pd;
uint32 netmask;
PDict(BPF_Program) filters;
struct pcap_pkthdr current_hdr; struct pcap_pkthdr current_hdr;
struct pcap_pkthdr last_hdr; struct pcap_pkthdr last_hdr;