]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #4431: JSON formatting for data logging
authorOleksii Shumeiko -X (oshumeik - SOFTSERVE INC at Cisco) <oshumeik@cisco.com>
Wed, 4 Sep 2024 10:00:40 +0000 (10:00 +0000)
committerOleksii Shumeiko -X (oshumeik - SOFTSERVE INC at Cisco) <oshumeik@cisco.com>
Wed, 4 Sep 2024 10:00:40 +0000 (10:00 +0000)
Merge in SNORT/snort3 from ~OSHUMEIK/snort3:extr_json to master

Squashed commit of the following:

commit 2bd2c95c4d5ab3b10dc1600f79e77e0c5fe2ab9e
Author: Oleksii Shumeiko <oshumeik@cisco.com>
Date:   Wed Sep 4 09:56:08 2024 +0300

    extractor: mention a field in initialization list

    Despite that initialization order is defined by class fields only,
    an explicit initializer in the list will add cross-validation.

commit 7eeaf9b8814aab9331a0e6d507c7e356ee8a9582
Author: Oleksii Shumeiko <oshumeik@cisco.com>
Date:   Tue Sep 3 13:50:18 2024 +0300

    extractor: add unit tests for enum types

commit 08928ef3bddf3f61878fe2a936d6be96f2a410e1
Author: Oleksii Shumeiko <oshumeik@cisco.com>
Date:   Tue Sep 3 10:49:41 2024 +0300

    extractor: remove unused headers

commit 4a6f42fd87d00ce27d5b8b27add094921c85fd5e
Author: Oleksii Shumeiko <oshumeik@cisco.com>
Date:   Wed Aug 28 12:13:52 2024 +0300

    extractor: add json logger

commit b783608bc41247bd3d45e8acedfc87c5dea445c2
Author: Oleksii Shumeiko <oshumeik@cisco.com>
Date:   Wed Aug 28 12:00:25 2024 +0300

    extractor: fix local variable

    Declare a local variable to be static to not interfere at linking stage.

commit 077f8133e94079ec86ee5e053b96f2b45afa71de
Author: Oleksii Shumeiko <oshumeik@cisco.com>
Date:   Wed Aug 28 11:55:23 2024 +0300

    extractor: take a note of FIXIT-P in key points

commit 08ed77f484fa19a4cd384af4ee62565a5ea81976
Author: Oleksii Shumeiko <oshumeik@cisco.com>
Date:   Wed Aug 28 11:47:49 2024 +0300

    extractor: add field name to logging function

    Field order is not preset for all formattings.
    Thus, a caller must pass a field name for proper logging.

commit 7a81360468ffe1431c4f93d384b295f659c7b93e
Author: Oleksii Shumeiko <oshumeik@cisco.com>
Date:   Tue Aug 27 15:21:38 2024 +0300

    extractor: fix guard-macro names

12 files changed:
src/network_inspectors/extractor/CMakeLists.txt
src/network_inspectors/extractor/extractor.cc
src/network_inspectors/extractor/extractor_csv_logger.cc
src/network_inspectors/extractor/extractor_csv_logger.h
src/network_inspectors/extractor/extractor_http_event_handler.cc
src/network_inspectors/extractor/extractor_json_logger.cc [new file with mode: 0644]
src/network_inspectors/extractor/extractor_json_logger.h [new file with mode: 0644]
src/network_inspectors/extractor/extractor_logger.cc
src/network_inspectors/extractor/extractor_logger.h
src/network_inspectors/extractor/extractor_service.cc
src/network_inspectors/extractor/extractor_service.h
src/network_inspectors/extractor/extractor_writer.cc

index f6451cc7c54ec2a8dc6c437fe2f2172dddf4cc62..20bbc977585349b74591d1f3d5966482299bf9b5 100644 (file)
@@ -5,6 +5,8 @@ set( FILE_LIST
     extractor_csv_logger.h
     extractor_event_handlers.h
     extractor_http_event_handler.cc
+    extractor_json_logger.cc
+    extractor_json_logger.h
     extractor_logger.cc
     extractor_logger.h
     extractor_service.cc
index b569f5005efd659eedbabc5b6c45dbf76b671892..f2f6fddff598e469a68d0088c42c29ed39824d7f 100644 (file)
@@ -65,7 +65,7 @@ static const Parameter extractor_proto_params[] =
 
 static const Parameter s_params[] =
 {
-    { "formatting", Parameter::PT_ENUM, "csv", "csv",
+    { "formatting", Parameter::PT_ENUM, "csv | json", "csv",
       "output format for extractor" },
 
     { "output", Parameter::PT_ENUM, "stdout", "stdout",
index 4b9c96960ce1baf2a3f0d18e2b5b46c2bbb35393..d497b9cd6cba78a72a4a5bade49b7079b556af68 100644 (file)
 
 #include "extractor_csv_logger.h"
 
-#include <algorithm>
 #include <cassert>
+#include <string>
 
-THREAD_LOCAL bool first_write;
+static THREAD_LOCAL bool first_write;
 
 void CsvExtractorLogger::add_header()
 {
@@ -56,7 +56,7 @@ void CsvExtractorLogger::close_record()
     writer->unlock();
 }
 
-void CsvExtractorLogger::add_field(const snort::Value& v)
+void CsvExtractorLogger::add_field(const char*, const snort::Value& v)
 {
     switch (v.get_type())
     {
index e996ec44114b393a7e1322cb839e6a713412c374..76f5e4fc4c2c1b971a83a54f59fbde54040e2478 100644 (file)
@@ -17,8 +17,8 @@
 //--------------------------------------------------------------------------
 // csv_logger.h author Anna Norokh <anorokh@cisco.com>
 
-#ifndef CSV_LOGGER_H
-#define CSV_LOGGER_H
+#ifndef EXTRACTOR_CSV_LOGGER_H
+#define EXTRACTOR_CSV_LOGGER_H
 
 #include "framework/value.h"
 
@@ -38,7 +38,7 @@ public:
     ~CsvExtractorLogger() override;
 
     void add_header() override;
-    void add_field(const snort::Value&) override;
+    void add_field(const char*, const snort::Value&) override;
     void open_record() override;
     void close_record() override;
 
index b76c50f46a10f4faade4939082d8561b5bd2c1b5..dd3acd320a837f65e19e8670eceb943788e3b4d2 100644 (file)
@@ -35,6 +35,7 @@
 
 using namespace snort;
 
+// FIXIT-P: inspector's data passes many functions before getting to the logger
 
 typedef Value* (*GetFunc) (DataEvent*, Packet*, Flow*);
 
@@ -236,8 +237,9 @@ void HttpExtractorEventHandler::handle(DataEvent& event, Flow* flow)
     logger.open_record();
     for (const auto& field : fields)
     {
+        // FIXIT-P: this is way too slow (a map with a string key type)
         auto val = std::unique_ptr<Value>(event_getters[field](&event, p, flow));
-        logger.add_field(*val.get());
+        logger.add_field(field.c_str(), *val.get());
     }
     logger.close_record();
 
diff --git a/src/network_inspectors/extractor/extractor_json_logger.cc b/src/network_inspectors/extractor/extractor_json_logger.cc
new file mode 100644 (file)
index 0000000..a286bc8
--- /dev/null
@@ -0,0 +1,62 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2024-2024 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.
+//--------------------------------------------------------------------------
+// json_logger.cc author Cisco
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "extractor_json_logger.h"
+
+#include <cassert>
+
+void JsonExtractorLogger::open_record()
+{
+    oss.str("");
+    js.open();
+}
+
+void JsonExtractorLogger::close_record()
+{
+    js.close();
+
+    writer->lock();
+    writer->write(oss.str().c_str());
+    writer->unlock();
+}
+
+void JsonExtractorLogger::add_field(const char* f, const snort::Value& v)
+{
+    switch (v.get_type())
+    {
+    case snort::Value::ValueType::VT_UNUM:
+        js.uput(f, v.get_uint64());
+        break;
+
+    case snort::Value::ValueType::VT_STR:
+        js.put(f, v.get_string());
+        break;
+
+    case snort::Value::ValueType::VT_BOOL: // fallthrough
+    case snort::Value::ValueType::VT_NUM:  // fallthrough
+    case snort::Value::ValueType::VT_REAL: // fallthrough
+    default:
+        assert(false);
+        break;
+    }
+}
diff --git a/src/network_inspectors/extractor/extractor_json_logger.h b/src/network_inspectors/extractor/extractor_json_logger.h
new file mode 100644 (file)
index 0000000..ca98925
--- /dev/null
@@ -0,0 +1,52 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2024-2024 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.
+//--------------------------------------------------------------------------
+// json_logger.h author Cisco
+
+#ifndef EXTRACTOR_JSON_LOGGER_H
+#define EXTRACTOR_JSON_LOGGER_H
+
+#include <sstream>
+
+#include "framework/value.h"
+#include "helpers/json_stream.h"
+
+#include "extractor_logger.h"
+#include "extractor_writer.h"
+
+class JsonExtractorLogger : public ExtractorLogger
+{
+public:
+    JsonExtractorLogger(OutputType o_type, const std::vector<std::string>& fields)
+        : ExtractorLogger(fields), writer(ExtractorWriter::make_writer(o_type)), oss(), js(oss)
+    { }
+
+    ~JsonExtractorLogger() override
+    { delete writer; }
+
+    void add_field(const char*, const snort::Value&) override;
+    void open_record() override;
+    void close_record() override;
+
+private:
+    ExtractorWriter* const writer;
+    std::ostringstream oss;
+    snort::JsonStream js;
+
+};
+
+#endif
index fa8d7cae17641c1eea9fd0fc173fb16972702c96..9cfccd9a4146e658628ee95875bffbc7b549f175 100644 (file)
@@ -26,6 +26,7 @@
 #include <cassert>
 
 #include "extractor_csv_logger.h"
+#include "extractor_json_logger.h"
 
 ExtractorLogger* ExtractorLogger::make_logger(FormatType f_type, OutputType o_type,
     const std::vector<std::string>& fields)
@@ -40,9 +41,39 @@ ExtractorLogger* ExtractorLogger::make_logger(FormatType f_type, OutputType o_ty
     case FormatType::CSV:
         logger = new CsvExtractorLogger(o_type, fields);
         break;
+    case FormatType::JSON:
+        logger = new JsonExtractorLogger(o_type, fields);
+        break;
+    case FormatType::MAX: // fallthrough
+    default:
+        break;
     }
 
     assert(logger);
 
     return logger;
 }
+
+#ifdef UNIT_TEST
+
+#include "catch/snort_catch.h"
+
+#include <memory.h>
+
+using namespace snort;
+
+TEST_CASE("Format Type", "[extractor]")
+{
+    SECTION("to string")
+    {
+        FormatType csv = FormatType::CSV;
+        FormatType json = FormatType::JSON;
+        FormatType max = FormatType::MAX;
+
+        CHECK_FALSE(strcmp("csv", csv.c_str()));
+        CHECK_FALSE(strcmp("json", json.c_str()));
+        CHECK_FALSE(strcmp("(not set)", max.c_str()));
+    }
+}
+
+#endif
index 359a183fff6342f16a94b99b05cc26ff4762e13a..68e4e35e3b5d6593b5c8502c5039c781640430b6 100644 (file)
@@ -32,7 +32,9 @@ class FormatType
 public:
     enum Value : uint8_t
     {
-        CSV
+        CSV,
+        JSON,
+        MAX
     };
 
     FormatType() = default;
@@ -48,6 +50,9 @@ public:
         {
         case CSV:
             return "csv";
+        case JSON:
+            return "json";
+        case MAX: // fallthrough
         default:
             return "(not set)";
         }
@@ -71,7 +76,8 @@ public:
 
     virtual void add_header() {}
     virtual void add_footer() {}
-    virtual void add_field(const snort::Value&) {}
+    // FIXIT-P: replace Value type designed for parsing with a better type
+    virtual void add_field(const char*, const snort::Value&) {}
 
     virtual void open_record() {}
     virtual void close_record() {}
index e9e1fabe5651778a51570b299581d39da3058c52..3c771e60a731fb213c5153d965dd416a89511d87 100644 (file)
@@ -179,3 +179,27 @@ HttpExtractorService::HttpExtractorService(uint32_t tenant, const std::vector<st
         }
     }
 }
+
+#ifdef UNIT_TEST
+
+#include "catch/snort_catch.h"
+
+#include <memory.h>
+
+using namespace snort;
+
+TEST_CASE("Service Type", "[extractor]")
+{
+    SECTION("to string")
+    {
+        ServiceType http = ServiceType::HTTP;
+        ServiceType undef = ServiceType::UNDEFINED;
+        ServiceType max = ServiceType::MAX;
+
+        CHECK_FALSE(strcmp("http", http.c_str()));
+        CHECK_FALSE(strcmp("(not set)", undef.c_str()));
+        CHECK_FALSE(strcmp("(not set)", max.c_str()));
+    }
+}
+
+#endif
index 2bb560ea17d6bb1d3991a5203cca44fc00f2f63a..6725f462f13466e992f1af24c31b99f3a784bdcd 100644 (file)
@@ -17,8 +17,8 @@
 //--------------------------------------------------------------------------
 // extractor_service.h author Maya Dagon <mdagon@cisco.com>
 
-#ifndef EXTRACTOR_SERVICES_H
-#define EXTRACTOR_SERVICES_H
+#ifndef EXTRACTOR_SERVICE_H
+#define EXTRACTOR_SERVICE_H
 
 #include <algorithm>
 #include <string>
@@ -68,6 +68,7 @@ struct ServiceBlueprint
     std::vector<std::string> supported_fields;
 };
 
+// FIXIT-P: make a template with Logger and Writer as parameters
 class ExtractorService
 {
 public:
index 11e5216742d2fe5af963b417c5cb509b59260094..227b65f6cc7f4d234d342d20c7e320d8265dde93 100644 (file)
@@ -34,3 +34,25 @@ ExtractorWriter* ExtractorWriter::make_writer(OutputType o_type)
         return nullptr;
     }
 }
+
+#ifdef UNIT_TEST
+
+#include "catch/snort_catch.h"
+
+#include <memory.h>
+
+using namespace snort;
+
+TEST_CASE("Output Type", "[extractor]")
+{
+    SECTION("to string")
+    {
+        OutputType std = OutputType::STD;
+        OutputType max = OutputType::MAX;
+
+        CHECK_FALSE(strcmp("stdout", std.c_str()));
+        CHECK_FALSE(strcmp("(not set)", max.c_str()));
+    }
+}
+
+#endif