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

@ -1,131 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#include "config.h"
#include "util.h"
#include "BPF_Program.h"
#ifdef DONT_HAVE_LIBPCAP_PCAP_FREECODE
extern "C" {
#include "pcap-int.h"
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;
}
return 0;
}
pcap_t* pcap_open_dead(int linktype, int snaplen)
{
pcap_t* p;
p = (pcap_t*) malloc(sizeof(*p));
if ( ! p )
return 0;
memset(p, 0, sizeof(*p));
p->fd = -1;
p->snapshot = snaplen;
p->linktype = linktype;
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;
p = pcap_open_dead(linktype_arg, snaplen_arg);
if ( ! p )
return -1;
ret = pcap_compile(p, program, buf, optimize, mask);
pcap_close(p);
return ret;
}
}
#endif
BPF_Program::BPF_Program() : m_compiled(), m_program()
{
}
BPF_Program::~BPF_Program()
{
FreeCode();
}
bool BPF_Program::Compile(pcap_t* pcap, const char* filter, uint32 netmask,
char* errbuf, unsigned int errbuf_len, bool optimize)
{
if ( ! pcap )
return false;
FreeCode();
if ( pcap_compile(pcap, &m_program, (char *) filter, optimize, netmask) < 0 )
{
if ( errbuf )
safe_snprintf(errbuf, errbuf_len,
"pcap_compile(%s): %s", filter,
pcap_geterr(pcap));
return false;
}
m_compiled = true;
return true;
}
bool BPF_Program::Compile(int snaplen, int linktype, const char* filter,
uint32 netmask, char* errbuf, bool optimize)
{
FreeCode();
#ifdef LIBPCAP_PCAP_COMPILE_NOPCAP_HAS_ERROR_PARAMETER
char my_error[PCAP_ERRBUF_SIZE];
int err = pcap_compile_nopcap(snaplen, linktype, &m_program,
(char *) filter, optimize, netmask, error);
if ( err < 0 && errbuf )
safe_strncpy(errbuf, my_errbuf, PCAP_ERRBUF_SIZE);
#else
int err = pcap_compile_nopcap(snaplen, linktype, &m_program,
(char*) filter, optimize, netmask);
#endif
if ( err == 0 )
m_compiled = true;
return err == 0;
}
bpf_program* BPF_Program::GetProgram()
{
return m_compiled ? &m_program : 0;
}
void BPF_Program::FreeCode()
{
if ( m_compiled )
{
#ifdef DONT_HAVE_LIBPCAP_PCAP_FREECODE
pcap_freecode(NULL, &m_program);
#else
pcap_freecode(&m_program);
#endif
m_compiled = false;
}
}

View file

@ -1,52 +0,0 @@
// See the file "COPYING" in the main distribution directory for copyright.
#ifndef bpf_program_h
#define bpf_program_h
extern "C" {
#include <pcap.h>
}
#include "util.h"
// BPF_Programs are an abstraction around struct bpf_program,
// to create a clean facility for creating, compiling, and
// freeing such programs.
class BPF_Program {
public:
// Creates an empty, uncompiled BPF program.
BPF_Program();
~BPF_Program();
// Creates a BPF program for the given pcap handle.
// Parameters are like in pcap_compile(). Returns true
// for successful compilation, false otherwise.
bool Compile(pcap_t* pcap, const char* filter, uint32 netmask,
char* errbuf = 0, unsigned int errbuf_len = 0,
bool optimize = true);
// Creates a BPF program when no pcap handle is around,
// similarly to pcap_compile_nopcap(). Parameters are
// similar. Returns true on success.
bool Compile(int snaplen, int linktype, const char* filter,
uint32 netmask, char* errbuf = 0, bool optimize = true);
// Returns true if this program currently contains compiled
// code, false otherwise.
bool IsCompiled() { return m_compiled; }
// Accessor to the compiled program. Returns nil when
// no program is currently compiled.
bpf_program* GetProgram();
protected:
void FreeCode();
// (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;
struct bpf_program m_program;
};
#endif

View file

@ -4,5 +4,5 @@ include(BroPlugin)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
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()

View file

@ -37,13 +37,6 @@ void PcapSource::Close()
if ( ! pd )
return;
BPF_Program* code;
IterCookie* cookie = filters.InitForIteration();
while ( (code = filters.NextEntry(cookie)) )
delete code;
filters.Clear();
pcap_close(pd);
pd = 0;
last_data = 0;
@ -56,10 +49,6 @@ void PcapSource::OpenLive()
char errbuf[PCAP_ERRBUF_SIZE];
char tmp_errbuf[PCAP_ERRBUF_SIZE];
#if 0
filter_type = ft;
#endif
// Determine interface if not specified.
if ( props.path.empty() )
props.path = pcap_lookupdev(tmp_errbuf);
@ -74,7 +63,7 @@ void PcapSource::OpenLive()
// Determine network and netmask.
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 interface; and libpcap doesn't have any useful notion
@ -82,7 +71,7 @@ void PcapSource::OpenLive()
// just kludge around the error :-(.
// sprintf(errbuf, "pcap_lookupnet %s", tmp_errbuf);
// return;
netmask = 0xffffff00;
props.netmask = 0xffffff00;
}
// 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);
if ( PrecompileFilter(0, props.filter) && SetFilter(0) )
{
SetHdrSize();
SetHdrSize();
if ( ! pd )
// Was closed, couldn't get header size.
return;
Info(fmt("listening on %s, capture length %d bytes\n", props.path.c_str(), SnapLen()));
}
else
Close();
if ( ! pd )
// Was closed, couldn't get header size.
return;
props.is_live = true;
Opened(props);
Info(fmt("listening on %s, capture length %d bytes\n", props.path.c_str(), SnapLen()));
}
void PcapSource::OpenOffline()
{
char errbuf[PCAP_ERRBUF_SIZE];
#if 0
filter_type = ft;
#endif
pd = pcap_open_offline(props.path.c_str(), errbuf);
if ( ! pd )
@ -146,25 +126,16 @@ void PcapSource::OpenOffline()
return;
}
if ( PrecompileFilter(0, props.filter) && SetFilter(0) )
{
SetHdrSize();
SetHdrSize();
if ( ! pd )
// Was closed, unknown link layer type.
return;
if ( ! pd )
// Was closed, unknown link layer type.
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 )
InternalError("OS does not support selectable pcap fd");
}
else
Close();
if ( props.selectable_fd < 0 )
InternalError("OS does not support selectable pcap fd");
props.is_live = false;
Opened(props);
@ -211,31 +182,7 @@ void PcapSource::DoneWithPacket(Packet* pkt)
int PcapSource::PrecompileFilter(int index, const std::string& filter)
{
if ( ! pd )
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;
return PktSrc::PrecompileBPFFilter(index, filter).
}
int PcapSource::SetFilter(int index)
@ -245,9 +192,7 @@ int PcapSource::SetFilter(int index)
char errbuf[PCAP_ERRBUF_SIZE];
HashKey* hash = new HashKey(HashKey(bro_int_t(index)));
BPF_Program* code = filters.Lookup(hash);
delete hash;
BPF_Program* code = GetFilter(index);
if ( ! code )
{

View file

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