]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
packet capture works internally. no external interface. bpf under dev
authorCarter Waxman <cwaxman@cisco.com>
Wed, 20 Apr 2016 18:44:45 +0000 (14:44 -0400)
committerCarter Waxman <cwaxman@cisco.com>
Wed, 20 Apr 2016 18:44:45 +0000 (14:44 -0400)
src/network_inspectors/CMakeLists.txt
src/network_inspectors/Makefile.am
src/network_inspectors/dev_notes.txt
src/network_inspectors/network_inspectors.cc
src/network_inspectors/packet_capture/CMakeLists.txt [new file with mode: 0644]
src/network_inspectors/packet_capture/Makefile.am [new file with mode: 0644]
src/network_inspectors/packet_capture/capture_module.cc [new file with mode: 0644]
src/network_inspectors/packet_capture/capture_module.h [new file with mode: 0644]
src/network_inspectors/packet_capture/packet_capture.cc [new file with mode: 0644]
src/network_inspectors/packet_capture/packet_capture.h [new file with mode: 0644]

index 5271c44808ee3c744f4de6f81109d027e2d3116e..7c2d6f4dfe4dee7be057e5856b8d53d1b70dfb21 100644 (file)
@@ -2,6 +2,7 @@
 add_subdirectory(arp_spoof)
 add_subdirectory(binder)
 add_subdirectory(normalize)
+add_subdirectory(packet_capture)
 add_subdirectory(perf_monitor)
 add_subdirectory(port_scan)
 add_subdirectory(reputation)
@@ -19,10 +20,11 @@ add_library( network_inspectors STATIC
 
 target_link_libraries( network_inspectors
     ${STATIC_INSPECTOR_LIBS}
-    port_scan
     binder
-    perf_monitor
     normalize
+    packet_capture
+    perf_monitor
+    port_scan
     reputation 
     stream_tcp
 )
index 492a42c966480488d72ad3781611f6e23f9f8ba7..b03fff1b14bb466ab77f21f90b5aad49ed23352e 100644 (file)
@@ -17,6 +17,7 @@ SUBDIRS = \
 arp_spoof \
 binder \
 normalize \
+packet_capture \
 perf_monitor \
 port_scan \
 reputation
index 9da14ec63d3e2bf0e6b3af2605fa027d6ce6636a..6561bf516d7e22d0c17e0c27719584cc1a85e658 100644 (file)
@@ -18,6 +18,8 @@ as it processes each valid packet.
 normalize - A collection of IP/ICMP/TCP and potentially UDP frame level
 normalizations.
 
+packet_capture - A tool for dumping the wire packets that Snort receives.
+
 This entire set of inspectors is instantiated as a group via
 network_inspectors.cc
 
index d41f611a38f7bba0da6c99ae023a691d93cc5b68..d8da502d682fb1a5bc6d9695bd4798fed3e27602 100644 (file)
@@ -25,6 +25,7 @@
 
 extern const BaseApi* nin_binder;
 extern const BaseApi* nin_normalize;
+extern const BaseApi* nin_packet_capture;
 extern const BaseApi* nin_perf_monitor;
 extern const BaseApi* nin_port_scan_global;
 extern const BaseApi* nin_port_scan;
@@ -38,6 +39,7 @@ const BaseApi* network_inspectors[] =
 {
     nin_binder,
     nin_normalize,
+    nin_packet_capture,
     nin_perf_monitor,
     nin_port_scan_global,
     nin_port_scan,
diff --git a/src/network_inspectors/packet_capture/CMakeLists.txt b/src/network_inspectors/packet_capture/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0c0c3d0
--- /dev/null
@@ -0,0 +1,6 @@
+add_library ( packet_capture STATIC
+    capture_module.cc
+    capture_module.h
+    packet_capture.cc
+    packet_capture.h
+)
diff --git a/src/network_inspectors/packet_capture/Makefile.am b/src/network_inspectors/packet_capture/Makefile.am
new file mode 100644 (file)
index 0000000..3725bad
--- /dev/null
@@ -0,0 +1,6 @@
+
+noinst_LIBRARIES = libpacket_capture.a
+
+libpacket_capture_a_SOURCES = \
+capture_module.cc capture_module.h \
+packet_capture.cc packet_capture.h 
diff --git a/src/network_inspectors/packet_capture/capture_module.cc b/src/network_inspectors/packet_capture/capture_module.cc
new file mode 100644 (file)
index 0000000..6e40fe1
--- /dev/null
@@ -0,0 +1,56 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// capture_module.cc author Carter Waxman <rucombs@cisco.com>
+
+#include "capture_module.h"
+
+#include "profiler/profiler.h"
+#include "utils/util.h"
+
+THREAD_LOCAL SimpleStats cap_count_stats;
+THREAD_LOCAL ProfileStats cap_prof_stats;
+
+static const Parameter s_params[] =
+{
+    { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+CaptureModule::CaptureModule() :
+    Module(CAPTURE_NAME, CAPTURE_HELP, s_params)
+{ }
+
+ProfileStats* CaptureModule::get_profile() const
+{ return &cap_prof_stats; }
+
+bool CaptureModule::set(const char*, Value& v, SnortConfig*)
+{
+    return true;
+}
+
+void CaptureModule::get_config(CaptureConfig& cfg)
+{
+    cfg = config;
+}
+
+const PegInfo* CaptureModule::get_pegs() const
+{ return simple_pegs; }
+
+PegCount* CaptureModule::get_counts() const
+{ return (PegCount*)&cap_count_stats; }
+
diff --git a/src/network_inspectors/packet_capture/capture_module.h b/src/network_inspectors/packet_capture/capture_module.h
new file mode 100644 (file)
index 0000000..845c2c5
--- /dev/null
@@ -0,0 +1,56 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// capture_module.h author Carter Waxman <cwaxman@cisco.com>
+
+#ifndef PERF_MODULE_H
+#define PERF_MODULE_H
+
+#include "framework/module.h"
+
+#define CAPTURE_NAME "packet_capture"
+#define CAPTURE_HELP "raw packet dumping facility"
+
+struct CaptureConfig
+{
+    std::string filter;
+};
+
+/* The Module Class for incorporation into Snort++ */
+class CaptureModule : public Module
+{
+public:
+    CaptureModule();
+
+    bool set(const char*, Value&, SnortConfig*) override;
+
+    const PegInfo* get_pegs() const override;
+    PegCount* get_counts() const override;
+    ProfileStats* get_profile() const override;
+
+    void get_config(CaptureConfig&);
+
+private:
+    CaptureConfig config;
+};
+
+extern THREAD_LOCAL SimpleStats cap_count_stats;
+extern THREAD_LOCAL ProfileStats cap_prof_stats;
+
+#endif
+
diff --git a/src/network_inspectors/packet_capture/packet_capture.cc b/src/network_inspectors/packet_capture/packet_capture.cc
new file mode 100644 (file)
index 0000000..0a93bf6
--- /dev/null
@@ -0,0 +1,411 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2016-2016 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// packet_cpture.cc author Carter Waxman <cwaxman@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "capture_module.h"
+
+#include <pcap/pcap.h>
+#include <string>
+
+#include "framework/inspector.h"
+#include "log/messages.h"
+#include "main/snort_config.h"
+#include "main/thread.h"
+#include "utils/util.h"
+
+#ifdef UNIT_TEST
+#include "catch/catch.hpp"
+#endif
+
+#define FILE_NAME "packet_capture.pcap"
+
+using namespace std;
+
+static THREAD_LOCAL FILE* fh = nullptr;
+static THREAD_LOCAL pcap_t* pcap = nullptr;
+static THREAD_LOCAL pcap_dumper_t* dumper = nullptr;
+static THREAD_LOCAL struct bpf_program bpf;
+
+static void open_file(const char* name, bool tmp = false)
+{
+    if ( !fh )
+    {
+        if ( tmp )
+            fh = tmpfile();
+        else
+            fh = fopen(name, "wb+");
+    }
+}
+
+//-------------------------------------------------------------------------
+// class stuff
+//-------------------------------------------------------------------------
+
+class PacketCapture : public Inspector
+{
+public:
+    PacketCapture(CaptureModule*) {};
+
+    
+    void eval(Packet*) override;
+    void tterm() override { close_file(); };
+
+    virtual void enable(string);
+    virtual void disable();
+
+protected:
+    virtual void open_file();
+    virtual void write_packet(Packet* p);
+    virtual void close_file();
+
+private:
+    bool enabled = false;
+    string bpf_expr;
+
+    void init_capture();
+};
+
+void PacketCapture::eval(Packet* p)
+{
+    if ( enabled )
+    {
+        if ( !fh )
+        {
+            open_file();
+            init_capture();
+        }
+
+        write_packet(p);
+    }
+    else
+    {
+        if ( fh )
+            close_file();
+    }
+}
+
+void PacketCapture::enable(string filter)
+{
+    bpf_expr = filter;
+    enabled = true;
+}
+
+void PacketCapture::disable()
+{
+    enabled = false;
+}
+
+void PacketCapture::open_file()
+{
+    string fname;
+
+    get_instance_file(fname, FILE_NAME);
+    ::open_file(fname.c_str());
+}
+
+void PacketCapture::init_capture()
+{
+    bool error = false;
+
+    pcap = pcap_open_dead(DLT_RAW, 65535);
+    dumper = pcap_dump_fopen(pcap, fh);
+
+    if ( pcap_compile(pcap, &bpf, bpf_expr.c_str(), 1, 0) == -1 )
+    {
+        ErrorMessage("Could not compile bpf filter.");
+        error = true;
+    }
+    else if ( pcap_setfilter(pcap, &bpf) == -1 )
+    {
+        ErrorMessage("Could not install bpf filter.");
+        error = true;
+    }
+    else if ( !dumper )
+    {
+        ErrorMessage("Could not open dump file.");
+        error = true;
+    }
+
+    if ( error )
+    {
+        ErrorMessage("Disabling packet capture.");
+        disable();
+        close_file();
+    }
+}
+
+void PacketCapture::close_file()
+{
+    if ( dumper )
+    {
+        pcap_dump_close(dumper);
+        dumper = nullptr;
+    }
+    if ( pcap )
+    {
+        free(pcap);
+        pcap = nullptr;
+    }
+    if ( fh )
+    {
+        fclose(fh);
+        fh = nullptr;
+    }
+}
+
+void PacketCapture::write_packet(Packet* p)
+{
+    struct pcap_pkthdr pkth;
+    pkth.caplen = p->pkth->caplen;
+    pkth.len = p->pkth->pktlen;
+    pkth.ts = p->pkth->ts;
+    pcap_dump((unsigned char*)dumper, &pkth, p->pkt);
+}
+
+//-------------------------------------------------------------------------
+// api stuff
+//-------------------------------------------------------------------------
+
+static Module* mod_ctor()
+{ return new CaptureModule; }
+
+static void mod_dtor(Module* m)
+{ delete m; }
+
+static Inspector* pc_ctor(Module* m)
+{
+    static THREAD_LOCAL unsigned s_init = true;
+
+    if ( !s_init )
+        return nullptr;
+
+    return new PacketCapture((CaptureModule*)m);
+}
+
+static void pc_dtor(Inspector* p)
+{ delete p; }
+
+static const InspectApi pc_api =
+{
+    {
+        PT_INSPECTOR,
+        sizeof(InspectApi),
+        INSAPI_VERSION,
+        0,
+        API_RESERVED,
+        API_OPTIONS,
+        CAPTURE_NAME,
+        CAPTURE_HELP,
+        mod_ctor,
+        mod_dtor
+    },
+    IT_PACKET,
+    (uint16_t)PktType::ANY,
+    nullptr, // buffers
+    nullptr, // service
+    nullptr, // pinit
+    nullptr, // pterm
+    nullptr, // tinit
+    nullptr, // tterm
+    pc_ctor,
+    pc_dtor,
+    nullptr, // ssn
+    nullptr  // reset
+};
+
+const BaseApi* nin_packet_capture = &pc_api.base;
+
+#ifdef UNIT_TEST
+
+class MockPacketCapture : public PacketCapture
+{
+public:
+    bool write_packet_called = false;
+    MockPacketCapture(CaptureModule* m) : PacketCapture(m) {};
+    
+protected:
+    void open_file() override
+    {
+        if ( !fh )
+            ::open_file(nullptr, true);
+    };
+
+    void write_packet(Packet* p) override
+    {
+        if ( p )
+            PacketCapture::write_packet(p);
+        write_packet_called = true;
+    };
+};
+
+TEST_CASE("toggle", "[PacketCapture]")
+{
+    CaptureModule mod;
+    MockPacketCapture cap(&mod);
+    
+    cap.write_packet_called = false;
+    cap.eval(nullptr);
+    CHECK( !cap.write_packet_called );
+
+    cap.write_packet_called = false;
+    cap.enable("");
+    cap.eval(nullptr);
+    CHECK( cap.write_packet_called );
+    
+    cap.write_packet_called = false;
+    cap.disable();
+    cap.eval(nullptr);
+    CHECK( !cap.write_packet_called );
+}
+
+TEST_CASE("lazy file handling", "[PacketCapture]")
+{
+    CaptureModule mod;
+    MockPacketCapture cap(&mod);
+    
+    CHECK( !fh );
+
+    cap.eval(nullptr);
+    CHECK( !fh );
+
+    cap.enable("");
+    CHECK( !fh );
+
+    cap.eval(nullptr);
+    CHECK( fh );
+
+    cap.disable();
+    CHECK( fh );
+
+    cap.eval(nullptr);
+    CHECK( !fh );
+}
+
+TEST_CASE("pcap init", "[PacketCapture]")
+{
+    CaptureModule mod;
+    MockPacketCapture cap(&mod);
+    
+    cap.enable("");
+    cap.eval(nullptr);
+
+    fseek(fh, 0, SEEK_SET);
+    auto pcap = pcap_fopen_offline(fh, nullptr);
+
+    CHECK( pcap );
+
+    free(pcap);
+
+    cap.disable();
+    cap.eval(nullptr);
+}
+
+TEST_CASE("write packet", "[PacketCapture]")
+{
+    const uint8_t cooked[] = "AbCdEfGhIjKlMnOpQrStUvWxYz";
+    struct pcap_pkthdr hdr;
+
+    Packet p;
+    DAQ_PktHdr_t daq_hdr;
+    p.pkt = cooked;
+    p.pkth = &daq_hdr;
+
+    daq_hdr.caplen = sizeof(cooked);
+    daq_hdr.pktlen = sizeof(cooked);
+    time_t ts = time(nullptr);
+
+    CaptureModule mod;
+    MockPacketCapture cap(&mod);
+    
+    cap.enable("");
+    cap.eval(&p);
+
+    fseek(fh, 0, SEEK_SET);
+    auto pcap = pcap_fopen_offline(fh, nullptr);
+    auto packet = pcap_next(pcap, &hdr);
+
+    REQUIRE( packet );
+    CHECK( !memcmp(cooked, packet, fmax(hdr.caplen, sizeof(cooked))) );
+
+    free(pcap);
+
+    cap.disable();
+    cap.eval(nullptr);
+}
+
+TEST_CASE("bpf filter", "[PacketCapture]")
+{
+    const uint8_t match[] =
+        //ethernet
+        "\xfc\x4d\xd4\x3d\xdc\xb8\x3c\x08\xf6\x2d\x6d\xbf\x08\x00"
+
+        //ipv4
+        "\x45\x00\x00\x14\x96\x22\x40\x00\x39\x06\xb1\xeb\x0a\x52\xf0\x52"
+        "\x0a\x96";
+
+    const uint8_t non_match[] =
+        //ethernet
+        "\xfc\x4d\xd4\x3d\xdc\xb8\x3c\x08\xf6\x2d\x6d\xbf\x08\x00"
+
+        //ipv4
+        "\x45\x00\x00\x14\x96\x22\x40\x00\x39\x06\xb1\xeb\x0b\x52\xf0\x52"
+        "\x0a\x96";
+
+    struct pcap_pkthdr hdr;
+
+    Packet p;
+    DAQ_PktHdr_t daq_hdr;
+    p.pkth = &daq_hdr;
+
+    daq_hdr.caplen = sizeof(match);
+    daq_hdr.pktlen = sizeof(match);
+    time_t ts = time(nullptr);
+
+    CaptureModule mod;
+    MockPacketCapture cap(&mod);
+    
+    cap.enable("ip host 10.82.240.82");
+    p.pkt = match;
+    cap.eval(&p);
+    p.pkt = non_match;
+    cap.eval(&p);
+    p.pkt = match;
+    cap.eval(&p);
+
+    fseek(fh, 0, SEEK_SET);
+    auto pcap = pcap_fopen_offline(fh, nullptr);
+
+    auto packet = pcap_next(pcap, &hdr);
+    REQUIRE( packet );
+    CHECK( !memcmp(match, packet, fmax(hdr.caplen, sizeof(match))) );
+
+    packet = pcap_next(pcap, &hdr);
+    REQUIRE( packet );
+    CHECK( !memcmp(match, packet, fmax(hdr.caplen, sizeof(match))) );
+
+    free(pcap);
+
+    cap.disable();
+    cap.eval(nullptr);
+}
+#endif
diff --git a/src/network_inspectors/packet_capture/packet_capture.h b/src/network_inspectors/packet_capture/packet_capture.h
new file mode 100644 (file)
index 0000000..a65c1f8
--- /dev/null
@@ -0,0 +1,27 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2016 Cisco and/or its affiliates. All rights reserved.
+// Copyright (C) 2002-2013 Sourcefire, Inc.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation.  You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+//--------------------------------------------------------------------------
+// packet_capture.h author Carter Waxman <cwaxman@cisco.com>
+
+#ifndef PACKET_CAPTURE_H
+#define PACKET_CAPTURE_H
+
+#include "capture_module.h"
+
+#endif
+