diff --git a/src/iosource/pcap/BPF_Program.cc b/src/iosource/BPF_Program.cc similarity index 100% rename from src/iosource/pcap/BPF_Program.cc rename to src/iosource/BPF_Program.cc diff --git a/src/iosource/pcap/BPF_Program.h b/src/iosource/BPF_Program.h similarity index 100% rename from src/iosource/pcap/BPF_Program.h rename to src/iosource/BPF_Program.h diff --git a/src/iosource/CMakeLists.txt b/src/iosource/CMakeLists.txt index 6bcfa16289..272ced7740 100644 --- a/src/iosource/CMakeLists.txt +++ b/src/iosource/CMakeLists.txt @@ -7,6 +7,7 @@ include_directories(BEFORE ) set(iosource_SRCS + BPF_Program.cc Component.cc Manager.cc PktDumper.cc diff --git a/src/iosource/PktSrc.cc b/src/iosource/PktSrc.cc index 0ec04b65d8..2e056a10ba 100644 --- a/src/iosource/PktSrc.cc +++ b/src/iosource/PktSrc.cc @@ -25,6 +25,10 @@ PktSrc::PktSrc() PktSrc::~PktSrc() { + BPF_Program* code; + IterCookie* cookie = filters.InitForIteration(); + while ( (code = filters.NextEntry(cookie)) ) + delete code; } const std::string& PktSrc::Path() const @@ -43,6 +47,11 @@ int PktSrc::LinkType() const return IsOpen() ? props.link_type : -1; } +uint32 PktSrc::Netmask() const + { + return IsOpen() ? props.netmask : PCAP_NETMASK_UNKNOWN; + } + int PktSrc::HdrSize() const { return IsOpen() ? props.hdr_size : -1; @@ -77,6 +86,12 @@ void PktSrc::Opened(const Properties& arg_props) props = arg_props; SetClosed(false); + if ( ! PrecompileFilter(0, props.filter) || ! SetFilter(0) ) + { + Close(); + return; + } + DBG_LOG(DBG_PKTIO, "Opened source %s", props.path.c_str()); } @@ -409,12 +424,50 @@ int PktSrc::ExtractNextPacketInternal() 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; } -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); } diff --git a/src/iosource/PktSrc.h b/src/iosource/PktSrc.h index d04ca24817..5dd51ab5f2 100644 --- a/src/iosource/PktSrc.h +++ b/src/iosource/PktSrc.h @@ -8,6 +8,10 @@ extern "C" { } #include "IOSource.h" +#include "BPF_Program.h" +#include "Dict.h" + +declare(PDict,BPF_Program); namespace iosource { @@ -29,6 +33,7 @@ public: const std::string& Filter() const; bool IsLive() const; int LinkType() const; + uint32 Netmask() const; const char* ErrorMsg() const; int HdrSize() const; int SnapLen() const; @@ -41,7 +46,22 @@ public: // going to be continued. 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 // current packet available. @@ -50,12 +70,15 @@ public: // Precompiles a filter and associates the given index with it. // Returns true on success, 0 if a problem occurred or filtering is // 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 // success, 0 if a problem occurred or the filtering is not // 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); @@ -68,7 +91,13 @@ protected: int selectable_fd; int link_type; int hdr_size; + uint32 netmask; bool is_live; + + Properties() + { + netmask = PCAP_NETMASK_UNKNOWN; + } }; struct Packet { @@ -113,6 +142,9 @@ private: bool have_packet; Packet current_packet; + // For BPF filtering support. + PDict(BPF_Program) filters; + // Only set in pseudo-realtime mode. double first_timestamp; double first_wallclock; diff --git a/src/iosource/pcap/CMakeLists.txt b/src/iosource/pcap/CMakeLists.txt index b43d51b0ca..1c57bb6ac9 100644 --- a/src/iosource/pcap/CMakeLists.txt +++ b/src/iosource/pcap/CMakeLists.txt @@ -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() diff --git a/src/iosource/pcap/Source.cc b/src/iosource/pcap/Source.cc index 271c3efcd7..67a73fbd8a 100644 --- a/src/iosource/pcap/Source.cc +++ b/src/iosource/pcap/Source.cc @@ -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 ) { diff --git a/src/iosource/pcap/Source.h b/src/iosource/pcap/Source.h index 03b75c1ca7..b9b61ac618 100644 --- a/src/iosource/pcap/Source.h +++ b/src/iosource/pcap/Source.h @@ -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;