]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2353 in SNORT/snort3 from ~SMINUT/snort3:tcp_fp_io to master
authorMasud Hasan (mashasan) <mashasan@cisco.com>
Wed, 26 Aug 2020 18:07:25 +0000 (18:07 +0000)
committerMasud Hasan (mashasan) <mashasan@cisco.com>
Wed, 26 Aug 2020 18:07:25 +0000 (18:07 +0000)
Squashed commit of the following:

commit d8f33db11b1589cf65dddc77fde9cb428f747e7e
Author: Silviu Minut <sminut@cisco.com>
Date:   Tue Jul 28 18:48:55 2020 -0400

    rna: tcp fingerprints configuration, storage, matching and event generation

23 files changed:
src/framework/decode_data.h
src/host_tracker/host_tracker.cc
src/host_tracker/host_tracker.h
src/network_inspectors/rna/CMakeLists.txt
src/network_inspectors/rna/dev_notes.txt
src/network_inspectors/rna/rna_config.h
src/network_inspectors/rna/rna_fingerprint.cc [new file with mode: 0644]
src/network_inspectors/rna/rna_fingerprint.h
src/network_inspectors/rna/rna_fingerprint_tcp.cc
src/network_inspectors/rna/rna_fingerprint_tcp.h
src/network_inspectors/rna/rna_fp_reader.h [deleted file]
src/network_inspectors/rna/rna_inspector.cc
src/network_inspectors/rna/rna_inspector.h
src/network_inspectors/rna/rna_logger.cc
src/network_inspectors/rna/rna_logger.h
src/network_inspectors/rna/rna_logger_common.h
src/network_inspectors/rna/rna_module.cc
src/network_inspectors/rna/rna_module.h
src/network_inspectors/rna/rna_name.h [moved from src/network_inspectors/rna/rna_fp_reader.cc with 54% similarity]
src/network_inspectors/rna/rna_pnd.cc
src/network_inspectors/rna/test/CMakeLists.txt
src/network_inspectors/rna/test/rna_module_mock.h
src/network_inspectors/rna/test/rna_module_test.cc

index 968e9d2f2339cd6d2e1ec8fc0c8f801e232b6524..e12560ab704858e0c20847333f46f2a7de52135e 100644 (file)
@@ -141,6 +141,9 @@ struct DecodeData
 
     inline PktType get_pkt_type() const
     { return type; }
+
+    inline bool dont_fragment() const
+    { return decode_flags & DECODE_MF; }
 };
 
 #endif
index fce17fc8370423e148a0f41c608ad26b15c4b598..8f9463cbc26ac2174c824efc9a9c819e1a248f9d 100644 (file)
@@ -259,6 +259,13 @@ void HostTracker::remove_inferred_services()
     }
 }
 
+bool HostTracker::add_tcp_fingerprint(uint32_t fpid)
+{
+    lock_guard<mutex> lck(host_tracker_lock);
+    auto result = tcp_fpids.emplace(fpid);
+    return result.second;
+}
+
 static inline string to_time_string(uint32_t p_time)
 {
     time_t raw_time = (time_t) p_time;
@@ -325,4 +332,12 @@ void HostTracker::stringify(string& str)
         while ( total-- )
             str += to_string(xport_protos[total]) + (total? ", " : "");
     }
+
+    total = tcp_fpids.size();
+    if ( total )
+    {
+        str += "\ntcp fingerprint: ";
+        for ( const auto& fpid : tcp_fpids )
+            str += to_string(fpid) + (--total ? ", " : "");
+    }
 }
index 8ce467481146b1248c92e9403baa210a3d118b24..bc45d0a0e06a8c75325f2c0f7a9c66b98ffbcb26 100644 (file)
@@ -28,6 +28,7 @@
 #include <cstring>
 #include <mutex>
 #include <list>
+#include <set>
 #include <vector>
 
 #include "framework/counts.h"
@@ -172,6 +173,8 @@ public:
 
     void remove_inferred_services();
 
+    bool add_tcp_fingerprint(uint32_t fpid);
+
     //  This should be updated whenever HostTracker data members are changed
     void stringify(std::string& str);
 
@@ -184,6 +187,8 @@ private:
     std::vector<uint16_t, HostCacheAllocIp<uint16_t>> network_protos;
     std::vector<uint8_t, HostCacheAllocIp<uint8_t>> xport_protos;
     std::vector<HostApplication, HostAppAllocator> services;
+    std::set<uint32_t, std::less<uint32_t>, HostCacheAllocIp<uint32_t>> tcp_fpids;
+
     bool vlan_tag_present = false;
     vlan::VlanTagHdr vlan_tag;
     HostType host_type;
index 3ab0b729779976349a00232e339d9aa627493010..7408e79db111c56ae04649ce6f2a0ed5ea5c2cde 100644 (file)
@@ -1,19 +1,19 @@
 set (RNA_INCLUDES
     rna_fingerprint.h
     rna_fingerprint_tcp.h
-    rna_fp_reader.h
+    rna_inspector.h
     rna_logger.h
+    rna_name.h
 )
 
 set ( RNA_SOURCES
     ${RNA_INCLUDES}
     rna_event_handler.cc
     rna_event_handler.h
+    rna_fingerprint.cc
     rna_fingerprint.h
     rna_fingerprint_tcp.cc
     rna_fingerprint_tcp.h
-    rna_fp_reader.cc
-    rna_fp_reader.h
     rna_inspector.cc
     rna_inspector.h
     rna_logger.cc
index 9530ac4939219c69e55598d38f957ad8c2466151..c575ccd2cb1d9fb3c786d80f7a109e279018ef52 100644 (file)
@@ -78,3 +78,144 @@ for setting the top level pointer to point at their own instantiated host tracke
 needs to be preserved until discover_network_ethernet calls generate_change_vlan_update with this
 newly-created host tracker as an argument. This host tracker is deleted at the top level, and we must
 not return prior to that to avoid leaking any host trackers.
+
+Fingerprints
+
+Fingerprints are a sequence of features in network traffic used to identify a host's operating system.
+Currently only tcp fingerprints are supported, but future work may include udp fingerprints and
+user agent fingerprints.
+
+Tcp fingerprints are specified in lua and read by snort at configure time.
+Only the SYN and SYN-ACK packets are used for fingerprint matching. We refer to the SYN packet
+as "client" and SYN_ACK packet as "server" traffic.
+
+A typical fingerprint looks like this:
+
+-- centos client
+{
+    fpid = 110005,
+    type = 10,
+    uuid = "2fc04d1a-a2c2-11e2-840f-850c4648cdef",
+    ttl = 64,
+    tcp_window = "5712-5760",
+    mss = "X",
+    id = "X",
+    topts = "2 4 8 3",
+    ws = "7",
+    df = false,
+},
+
+This particular example identifies the host as one of 'CentOS Linux version 5.5','CentOS','Linux','Linux 5.5'.
+Note that there is no reference to the actual operating system in the fingerprint, but
+the fpid or the uuid field can be used for this purpose.
+
+The fields are as follows:
+
+fpid: (int) a unique fingerprint identifier. If snort encounters another fingerprint with the same
+      fpid, it will display a warning message and skip it. This field can be used as an index
+      into a database or table with details in human readable form about the host.
+      This field is not used for matching the fingerprint to the network traffic.
+
+type:  (int) the type of traffic this fingerprint should be matched against. Currently, these types
+       are defined in rna_fingerprint.h:
+
+       enum FpType
+       {
+           FINGERPRINT_TYPE_SERVER = 1,
+           FINGERPRINT_TYPE_CLIENT = 2,
+           FINGERPRINT_TYPE_SERVER6 = 10,
+           FINGERPRINT_TYPE_CLIENT6 = 11,
+       };
+
+uuid:  (uuid string, not an arbitrary string) similar in purpose to fpid, e.g. we could store
+       the host details in a file named 2fc04d1a-a2c2-11e2-840f-850c4648cdef.
+       Not used in matching.
+
+ttl:   (int) time to live
+
+df:    (bool) don't fragment flag
+
+The remaining fields are space-separated lists of FpElement, each of FpElementType.
+See rna_fingerprint.h. Such a list might be "1 2-5 6" or simply "X", or "10". Snort
+infers the FpElementType from the input string, e.g. "2-5" will be interpreted as a
+FpElementType::RANGE, and "X" will be interpreted as FpElementType::DONT_CARE.
+See the FpElement::parse_value() function in rna_fingerprint.cc.
+
+Here are all possible element types, with examples for each case:
+
+FpElementType::RANGE:     "1" or "20-30"
+FpElementType::INCREMENT: "+5"
+FpElementType::SYN_MATCH: "SYN"
+FpElementType::RANDOM:    "R"
+FpElementType::DONT_CARE: "X"
+FpElementType::SYNTS:     "TS"
+
+For instance, the list
+
+"1 2-5 SYN"
+
+is a legal list, consisting of 3 elements: 1 (RANGE), 2-5 (RANGE) and SYN (SYN_MATCH).
+While the elements in this list are all valid, there are constraints on the
+type of the elements that make up the fingerprint fields. For instance,
+
+mss = "2 3 X"
+
+would result in error, because DONT_CARE ("X") is not allowed for the mss field.
+
+Here are the remaining fingerprint fields.
+
+tcp_window: the tcp window
+            FpElementType::RANGE
+
+            In the packet, the tcp window is a single integer value, but in the fingerprint
+            we can specify a range like tcp_window = "1234-4321".
+
+            Examples:
+            tcp_window = "1234-4321" -- will match any packet with tcp_window in the range
+            tcp_window = "5678"      -- will match only packets with tcp_window = 5678
+            tcp_window = "R"         -- error: RANDOM is not a valid tcp_window element
+
+mss:   maximum segment size
+       On the client: FpElementType::RANGE, FpFLementType::DONT_CARE
+       On the server: FpElementType::RANGE, FpElementType::DONT_CARE,
+                      FpElementType::SYN_MATCH, FpElementType::SYNTS
+
+       Examples:
+       fptype = 2, mss = "12-34" -- client traffic (fptype = 2) with mss beween 12 and 34
+       fptype = 2, mss = "X"     -- don't use mss for matching, so match anything from cient
+       fptype = 2, mss = "SYN"   -- error: client traffic (fptype = 2) but mss of type SYN_MATCH
+                                    only accepted for server traffic (fptyp1 = 1 or 10)
+       fptype = 1, mss = "TS"    -- OK: server traffic (fptype = 1) and mss of type SYNTS
+       mss = "+5"                -- eror: mss cannot be an INCREMENT type
+
+id:    ip id
+       FpElementType::RANGE
+       FpElementType::RANDOM
+       FpElementType::INCREMENT
+       FpElementType::DONT_CARE
+
+       Example:
+       id = "X"
+
+topts: tcp options
+       FpElementType::RANGE
+
+       These are defined by TcpOptCode in src/protocols/tcp_options.h.
+       The ones we use for fingerprint matcing are
+
+       - MAXSEG (2)
+       - WSCALE (3)
+       - SACKOK (4)
+       - TIMESTAMP (8)
+
+       We match (a) whether or not an option is set, (b) what its value is and (c) the order
+       of the options.
+
+       Example:
+       topts = [2 4 8 3] -- will match tcp packets with mss (2), window scale (4), sack OK (8)
+                            and timestamp (3) set, in this exact order. If we swap any two
+                            numbers in topts, the resulting fingerprint will no longer match.
+
+ws:    window scale
+       FpElementType::RANGE
+       FpElementType::DONT_CARE
index f6063cba132cf5bbd74d400539858c6c3092cb5f..b9626f243d8b195404dfc2abdf5bafe978f5d8d2 100644 (file)
 #ifndef RNA_CONFIG_H
 #define RNA_CONFIG_H
 
+namespace snort
+{
+class TcpFpProcessor;
+}
+
 struct RnaModuleConfig
 {
     std::string rna_conf_path;
-    std::string fingerprint_dir;
     bool enable_logger;
     bool log_when_idle;
+    snort::TcpFpProcessor* processor = nullptr;
 };
 
 // Give default values so that RNA can work even if rna_conf_path is not provided
diff --git a/src/network_inspectors/rna/rna_fingerprint.cc b/src/network_inspectors/rna/rna_fingerprint.cc
new file mode 100644 (file)
index 0000000..6ca4c27
--- /dev/null
@@ -0,0 +1,198 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 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.
+//--------------------------------------------------------------------------
+
+// rna_fingerprint.cc author Silviu Minut <sminut@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "rna_fingerprint.h"
+
+#include <cassert>
+#include <cstring>
+
+#ifdef UNIT_TEST
+#include "catch/snort_catch.h"
+#endif
+
+using namespace snort;
+using namespace std;
+
+FpElement::FpElement(const string& str) : type(FpElementType::INVALID)
+{
+    parse_value(str);
+}
+
+FpElement& FpElement::operator=(const std::string& str)
+{
+    type = FpElementType::INVALID;
+    parse_value(str);
+    return *this;
+}
+
+bool FpElement::operator==(const FpElement& y) const
+{
+    const FpElement& x=*this;
+    if (x.type != y.type)
+        return false;
+
+    switch (x.type)
+    {
+    case FpElementType::RANGE:
+        return (x.d.value == y.d.value) &&
+            (x.d.range.min == y.d.range.min) &&
+            (x.d.range.max == y.d.range.max);
+
+    case FpElementType::SYN_MATCH:
+    case FpElementType::INCREMENT:
+        return x.d.value == y.d.value;
+
+    default:
+        break;
+    }
+    return true;
+}
+
+void FpElement::parse_value(const std::string& str)
+{
+    FpElement& v=*this;
+
+    v.type = FpElementType::INVALID;
+
+    const char* data = str.c_str();
+    for (; *data && isspace(*data); data++);
+
+    if (*data == '+')
+    {
+        v.type = FpElementType::INCREMENT;
+        data++;
+        v.d.value = strtol(data, nullptr, 10);
+        return;
+    }
+    else if (strlen(data) >= sizeof(FP_SYN_KEY)-1 &&
+        !strncmp(data, FP_SYN_KEY, sizeof(FP_SYN_KEY)-1))
+    {
+        v.type = FpElementType::SYN_MATCH;
+        data += sizeof(FP_SYN_KEY)-1;
+        if (*data != '-')
+        {
+            v.d.value = 0;
+            return;
+        }
+        data++;
+        v.d.value = strtol(data, nullptr, 10);
+        return;
+    }
+    else if (strlen(data) >= sizeof(FP_RANDOM_KEY)-1 &&
+        !strncmp(data, FP_RANDOM_KEY, sizeof(FP_RANDOM_KEY)-1))
+    {
+        v.type = FpElementType::RANDOM;
+        return;
+    }
+    else if (strlen(data) >= sizeof(FP_DONT_CARE_KEY)-1 &&
+        !strncmp(data, FP_DONT_CARE_KEY, sizeof(FP_DONT_CARE_KEY)-1))
+    {
+        v.type = FpElementType::DONT_CARE;
+        return;
+    }
+    else if (strlen(data) >= sizeof(FP_SYN_TS_KEY)-1 &&
+        !strncmp(data, FP_SYN_TS_KEY, sizeof(FP_SYN_TS_KEY)-1))
+    {
+        v.type = FpElementType::SYNTS;
+        return;
+    }
+
+    // this converts "-1" to [0,1], in agreement with snort 2
+    if (const char* r=strchr(data, '-'))
+    {
+        v.type = FpElementType::RANGE;
+        string left(data, r);
+        string right(++r);
+        v.d.range.min = strtol(left.c_str(), nullptr, 10);
+        v.d.range.max = strtol(right.c_str(), nullptr, 10);
+    }
+    else
+    {
+        v.type = FpElementType::RANGE;
+        v.d.range.min = strtol(data, nullptr, 10);
+        v.d.range.max = v.d.range.min;
+    }
+
+    assert(v.type != FpElementType::INVALID);
+}
+
+#ifdef UNIT_TEST
+
+TEST_CASE("FpElement", "[rna_fingerprint]")
+{
+    FpElement fpe;
+    FpElement fpe_test;
+
+    // RANGE, single value
+    fpe_test = "10";
+    fpe.type = FpElementType::RANGE;
+    fpe.d.value = 10;
+    fpe.d.range.min = 10;
+    fpe.d.range.max = 10;
+    CHECK(fpe == fpe_test);
+
+    // RANGE, range
+    fpe_test = "1-20";
+    fpe.type = FpElementType::RANGE;
+    fpe.d.value = 0;
+    fpe.d.range.min = 1;
+    fpe.d.range.max = 20;
+    CHECK(fpe == fpe_test);
+
+    // INCREMENT
+    fpe_test = "+20";
+    fpe.type = FpElementType::INCREMENT;
+    fpe.d.value = 20;
+    CHECK(fpe == fpe_test);
+
+    // SYN_MATCH, key only
+    fpe_test = FP_SYN_KEY;
+    fpe.type = FpElementType::SYN_MATCH;
+    fpe.d.value = 0;
+    CHECK(fpe == fpe_test);
+
+    // SYN_MATCH, key and value
+    fpe_test = string(FP_SYN_KEY) + "-20";
+    fpe.type = FpElementType::SYN_MATCH;
+    fpe.d.value = 20;
+    CHECK(fpe == fpe_test);
+
+    fpe_test = FP_RANDOM_KEY;
+    fpe.type = FpElementType::RANDOM;
+    fpe.d.value = 20;
+    CHECK(fpe == fpe_test);
+
+    fpe_test = FP_DONT_CARE_KEY;
+    fpe.type = FpElementType::DONT_CARE;
+    fpe.d.value = 20;
+    CHECK(fpe == fpe_test);
+
+    fpe_test = FP_SYN_TS_KEY;
+    fpe.type = FpElementType::SYNTS;
+    fpe.d.value = 20;
+    CHECK(fpe == fpe_test);
+}
+
+#endif
+
index fad62ece675e98066d467727ace90037197f36a3..24424dbea06189ea1e89f07adbc584f342fb93fa 100644 (file)
 #ifndef RNA_FINGERPRINT_H
 #define RNA_FINGERPRINT_H
 
+#include <cstdint>
+#include <string>
 #include <uuid/uuid.h>
 
+#include "main/snort_types.h"
+
+#define FP_SYN_KEY          "SYN"
+#define FP_RANDOM_KEY       "R"
+#define FP_DONT_CARE_KEY    "X"
+#define FP_SYN_TS_KEY       "TS"
+
+#define MAXIMUM_FP_HOPS 32
+
 namespace snort
 {
 
 class FpFingerprint
 {
 public:
-    uint32_t fpid;
-    uint32_t fp_type;
+
+    enum FpType
+    {
+        FINGERPRINT_TYPE_DERIVED = 0,
+        FINGERPRINT_TYPE_SERVER = 1,
+        FINGERPRINT_TYPE_CLIENT = 2,
+        FINGERPRINT_TYPE_SMB = 3,
+        FINGERPRINT_TYPE_DHCP = 4,
+        FINGERPRINT_TYPE_USER = 5,
+        FINGERPRINT_TYPE_SCAN = 6,
+        FINGERPRINT_TYPE_APP = 7,
+        FINGERPRINT_TYPE_CONFLICT = 8,
+        FINGERPRINT_TYPE_MOBILE = 9,
+        FINGERPRINT_TYPE_SERVER6 = 10,
+        FINGERPRINT_TYPE_CLIENT6 = 11,
+        FINGERPRINT_TYPE_DHCP6 = 12,
+        FINGERPRINT_TYPE_USERAGENT = 13,
+        MAX_FINGERPRINT_TYPES = 14
+    };
+
+    uint32_t fpid = 0;
+    uint32_t fp_type = 0;
     uuid_t fpuuid;
-    uint8_t ttl;
+    uint8_t ttl = 0;
+
+    virtual ~FpFingerprint() { }
+
+    virtual void clear()
+    {
+        fpid = 0;
+        fp_type = 0;
+        uuid_clear(fpuuid);
+        ttl = 0;
+    }
+};
+
+enum FpElementType
+{
+    INVALID = -1,
+    RANGE = 1,
+    INCREMENT,
+    SYN_MATCH,
+    RANDOM,
+    DONT_CARE,
+    SYNTS
+};
+
+class SO_PUBLIC FpElement
+{
+public:
+
+    FpElement() = default;
+    FpElement(const std::string&);
+
+    FpElement& operator=(const FpElement& fpe) = default;
+    FpElement& operator=(const std::string& str);
+    bool operator==(const FpElement& y) const;
+
+    FpElementType type;
+    union
+    {
+        int value;
+        struct
+        {
+            int min;
+            int max;
+        } range;
+    } d;
+
+private:
+    void parse_value(const std::string&);
 };
 
 }
 
+class RawFingerprint
+{
+public:
+
+    uint32_t fpid = 0;
+    uint32_t fp_type = 0;
+    std::string fpuuid;
+    uint8_t ttl = 0;
+
+    std::string tcp_window;
+    std::string mss;
+    std::string id;
+    std::string topts;
+    std::string ws;
+    bool df = false;
+
+    void clear()
+    {
+        fpid = 0;
+        fp_type = 0;
+        fpuuid.clear();
+        ttl = 0;
+        tcp_window.clear();
+        mss.clear();
+        id.clear();
+        topts.clear();
+        ws.clear();
+        df=false;
+    }
+
+};
 
 #endif
index f26d1df3bf82da7ea73ff308468a45d2356faed7..e6931db1ae5705cf210143354f13dabb05937ef8 100644 (file)
 
 #include "rna_fingerprint_tcp.h"
 
+#include <sstream>
+
 #ifdef UNIT_TEST
 #include "catch/snort_catch.h"
 #endif
 
+#include "log/messages.h"
+#include "protocols/packet.h"
+#include "protocols/tcp.h"
+#include "protocols/tcp_options.h"
+
 using namespace snort;
 using namespace std;
 
-static TcpFpProcessor tcp_fp_processor;
+static THREAD_LOCAL TcpFpProcessor* tcp_fp_processor = nullptr;
 
-namespace snort
+unsigned RNAFlow::inspector_id = 0;
+
+static int parse_fp_element(const string& data, vector<FpElement>& fpe)
 {
+    istringstream in(data);
+    string tok;
+
+    while ( in >> tok )
+        fpe.emplace_back(tok);
+    return 1;
+}
 
 TcpFpProcessor* get_tcp_fp_processor()
 {
-    return &tcp_fp_processor;
+    return tcp_fp_processor;
+}
+
+void set_tcp_fp_processor(TcpFpProcessor* processor)
+{
+    tcp_fp_processor = processor;
+}
+
+namespace snort
+{
+
+TcpFingerprint::TcpFingerprint(const RawFingerprint& rfp)
+{
+    fpid = rfp.fpid;
+    fp_type = rfp.fp_type;
+    uuid_parse(rfp.fpuuid.c_str(), fpuuid);
+    ttl = rfp.ttl;
+
+    parse_fp_element(rfp.tcp_window, tcp_window);
+    parse_fp_element(rfp.mss, mss);
+    parse_fp_element(rfp.id, id);
+    parse_fp_element(rfp.topts, topts);
+    parse_fp_element(rfp.ws, ws);
+    df = rfp.df;
+}
+
+bool TcpFingerprint::operator==(const TcpFingerprint& y) const
+{
+    return (
+        fpid == y.fpid &&
+        fp_type == y.fp_type &&
+        !uuid_compare(fpuuid, y.fpuuid) &&
+        ttl == y.ttl &&
+        equal(tcp_window.begin(), tcp_window.end(), y.tcp_window.begin()) &&
+        equal(mss.begin(), mss.end(), y.mss.begin()) &&
+        equal(id.begin(), id.end(), y.id.begin()) &&
+        equal(topts.begin(), topts.end(), y.topts.begin()) &&
+        df == y.df);
+}
+
+void TcpFpProcessor::push(const TcpFingerprint& tfp)
+{
+    const auto& result = tcp_fps.emplace(make_pair(tfp.fpid, tfp));
+    if (!result.second)
+        WarningMessage("TcpFpProcessor: ignoring previously seen fingerprint id: %d\n", tfp.fpid);
+
 }
 
-void TcpFpProcessor::push(const vector<FpTcpFingerprint>& fplist, TCP_FP_MODE mode)
+void TcpFpProcessor::make_tcp_fp_tables(TCP_FP_MODE mode)
 {
-    vector<const FpTcpFingerprint*>* fptable = (mode == TCP_FP_MODE::SERVER ?
+    auto* fptable = (mode == TCP_FP_MODE::SERVER ?
         table_tcp_server : table_tcp_client);
 
-    for (const auto& tfp : fplist)
+    for (size_t i = 0; i < table_size; i++)
+        fptable[i].clear();
+
+    for (const auto& tfpit : tcp_fps)
     {
+        const auto& tfp = tfpit.second;
         for (const auto& fpe : tfp.tcp_window)
         {
             switch (fpe.type)
@@ -63,16 +128,579 @@ void TcpFpProcessor::push(const vector<FpTcpFingerprint>& fplist, TCP_FP_MODE mo
     }
 }
 
+const TcpFingerprint* TcpFpProcessor::get_tcp_fp(const FpTcpKey& key, uint8_t ttl,
+    TCP_FP_MODE mode) const
+{
+    uint8_t optorder[4];
+    uint8_t fp_optorder[4];
+    int optpos;
+    int fp_optpos;
+    int i;
+    uint32_t fptype;
+
+    const vector<const snort::TcpFingerprint*>* fptable;
+
+    if (mode == TCP_FP_MODE::SERVER)
+    {
+        fptable = table_tcp_server;
+        fptype = key.isIpv6 ?
+            FpFingerprint::FpType::FINGERPRINT_TYPE_SERVER6 :
+            FpFingerprint::FpType::FINGERPRINT_TYPE_SERVER;
+    }
+    else if (mode == TCP_FP_MODE::CLIENT)
+    {
+        fptable = table_tcp_client;
+        fptype = key.isIpv6 ?
+            FpFingerprint::FpType::FINGERPRINT_TYPE_CLIENT6 :
+            FpFingerprint::FpType::FINGERPRINT_TYPE_CLIENT;
+    }
+    else
+    {
+        ErrorMessage("TcpFpProcessor::get_tcp_fingerprint(): Invalid mode - %d", mode);
+        return nullptr;
+    }
+
+    const auto& tfpvec = fptable[key.tcp_window];
+    for (const auto& tfp : tfpvec)
+    {
+        if (tfp->fp_type != fptype )
+            continue;   // tfp
+
+        for (const auto& fpe_mss : tfp->mss)
+        {
+            switch (fpe_mss.type)
+            {
+            case FpElementType::RANGE:
+                if (key.mss >= fpe_mss.d.range.min &&
+                    key.mss <= fpe_mss.d.range.max)
+                {
+                    goto mssgood;
+                }
+                break;
+            case FpElementType::SYN_MATCH:
+                //if synmss is negative, it means that client didn't send MSS option
+                if ((key.synmss  >= 0)
+                    && ((!fpe_mss.d.value && key.mss <= key.synmss)
+                        || (fpe_mss.d.value && ((key.synmss < fpe_mss.d.value && key.mss <= key.synmss)
+                        || (key.synmss >= fpe_mss.d.value && key.mss <= fpe_mss.d.value)))))
+                {
+                    goto mssgood;
+                }
+                break;
+            case FpElementType::DONT_CARE:
+            case FpElementType::SYNTS:
+                goto mssgood;
+            default:
+                break;
+            }
+        }
+        continue;  // tfp
+
+    mssgood:
+        if (key.df == tfp->df &&
+            ttl <= tfp->ttl &&
+            (tfp->ttl < MAXIMUM_FP_HOPS || ttl >= (tfp->ttl - MAXIMUM_FP_HOPS)))
+        {
+            if (key.ws_pos >= 0)
+            {
+                for (const auto& fpe_ws : tfp->ws)
+                {
+                    switch (fpe_ws.type)
+                    {
+                    case FpElementType::RANGE:
+                        if (key.ws >= fpe_ws.d.range.min &&
+                            key.ws <= fpe_ws.d.range.max)
+                        {
+                            goto wsgood;
+                        }
+                        break;
+                    case FpElementType::DONT_CARE:
+                        goto wsgood;
+                    default:
+                        break;
+                    }
+                }
+                continue;  // tfp
+            }
+
+        wsgood:
+            if (mode == TCP_FP_MODE::SERVER)
+            {
+                //create array of options in the order seen in server packet
+                for (i=0, optpos=0; i<TCP_OPTLENMAX && optpos<4; i++)
+                {
+                    if (i == key.ws_pos)
+                        optorder[optpos++] = (uint8_t) tcp::TcpOptCode::WSCALE;
+                    else if (i == key.mss_pos)
+                        optorder[optpos++] = (uint8_t) tcp::TcpOptCode::MAXSEG;
+                    else if (i == key.sackok_pos)
+                        optorder[optpos++] = (uint8_t) tcp::TcpOptCode::SACKOK;
+                    else if (i == key.timestamp_pos)
+                        optorder[optpos++] = (uint8_t) tcp::TcpOptCode::TIMESTAMP;
+                }
+
+                //create array of options from fingerprint that were present in client packet.
+                //Ordered by fingerprint option order
+                fp_optpos = 0;
+                for (const auto& fpe_topts : tfp->topts)
+                {
+                    for (i=0; i<key.num_syn_tcpopts; i++)
+                    {
+                        if (key.syn_tcpopts[i] == fpe_topts.d.range.min)
+                        {
+                            fp_optorder[fp_optpos++] = key.syn_tcpopts[i];
+                            break;
+                        }
+                    }
+                }
+
+                //if number, type, or order of option in SYN mismatch those in FP,
+                //goto next check.
+                if (optpos != fp_optpos) goto check_ts;
+
+                for (i=0; i<optpos; i++)
+                {
+                    if (optorder[i] != fp_optorder[i]) goto check_ts;
+                }
+                return tfp;
+
+            check_ts:
+                //number and type of options didn't match between SYN and fingerprint.
+                //Ignore Timestamp option if present in SYN. Remaining processing is
+                //the same as the block above.
+                for (i=0; i<key.num_syn_tcpopts; i++)
+                {
+                    if (key.syn_tcpopts[i] == (uint8_t) tcp::TcpOptCode::TIMESTAMP)
+                    {
+                        break;
+                    }
+                }
+                if (i >= key.num_syn_tcpopts || key.syn_timestamp)
+                    continue;    // tfp
+
+                fp_optpos = 0;
+                for (const auto& fpe_topts : tfp->topts)
+                {
+                    for (i=0; i<key.num_syn_tcpopts; i++)
+                    {
+                        if (key.syn_tcpopts[i] == fpe_topts.d.range.min)
+                        {
+                            if (key.syn_tcpopts[i] != (uint8_t) tcp::TcpOptCode::TIMESTAMP)
+                                fp_optorder[fp_optpos++] = key.syn_tcpopts[i];
+                            break;
+                        }
+                    }
+                }
+                if (optpos != fp_optpos)
+                    continue;   // ftp
+
+                for (i=0; i<optpos; i++)
+                {
+                    if (optorder[i] != fp_optorder[i])
+                        continue; // tfp
+                }
+                return tfp;
+            }
+            else
+            {
+                for (i=0, optpos=0; i<TCP_OPTLENMAX && optpos<4; i++)
+                {
+                    if (i == key.ws_pos)
+                        optorder[optpos++] = (uint8_t) tcp::TcpOptCode::WSCALE;
+                    else if (i == key.mss_pos)
+                        optorder[optpos++] = (uint8_t) tcp::TcpOptCode::MAXSEG;
+                    else if (i == key.sackok_pos)
+                        optorder[optpos++] = (uint8_t) tcp::TcpOptCode::SACKOK;
+                    else if (i == key.timestamp_pos)
+                        optorder[optpos++] = (uint8_t) tcp::TcpOptCode::TIMESTAMP;
+                }
+
+                auto fpe_topts = tfp->topts.begin();
+                for (i = 0; i<optpos && fpe_topts != tfp->topts.end(); i++, fpe_topts++)
+                {
+                    if (optorder[i] != fpe_topts->d.range.min)
+                        break;
+                }
+
+                // if there were more elements in this fingerprint or
+                // there was a mismatch between this fingerprint and the
+                // key, go to the next fingerprint in the store
+                if (fpe_topts != tfp->topts.end() || i < optpos)
+                    continue;
+
+                return tfp;
+            }
+        }
+    }
+    return nullptr;
+}
+
+
+static int get_tcp_option(const Packet* p, tcp::TcpOptCode opt_code, int& pos)
+{
+    int maxops = (int) p->ptrs.tcph->options_len();
+    if (maxops < 0 || TCP_OPTLENMAX < maxops)
+    {
+        pos = -1;
+        return -1;
+    }
+
+    pos = 0;
+    tcp::TcpOptIterator opt_iter(p->ptrs.tcph, p);
+    for (const tcp::TcpOption& opt : opt_iter)
+    {
+        if (opt.code == opt_code)
+        {
+            switch (opt.code)
+            {
+            case tcp::TcpOptCode::MAXSEG:
+                return ntohs(*((const uint16_t*)(opt.data)));
+
+            case tcp::TcpOptCode::NOP:
+                return 1;
+
+            case tcp::TcpOptCode::SACKOK:
+                return 1;
+
+            case tcp::TcpOptCode::WSCALE:
+                return opt.data[0] & 0xff;
+
+            case tcp::TcpOptCode::TIMESTAMP:
+                return ntohs(*((const uint32_t*)(opt.data)));
+
+            default:
+                break;
+            }
+        }
+
+        pos++;
+    }
+
+    pos = -1;
+    return -1;
+}
+
+const TcpFingerprint* TcpFpProcessor::get(const Packet* p, RNAFlow* flowp) const
+{
+    FpTcpKey fpk;
+    bool mssOptionPresent = false;
+
+    bzero(&fpk, sizeof(FpTcpKey));
+
+    if (p->is_ip6())
+        fpk.isIpv6 = 1;
+
+    /* build a key for the lookup */
+    fpk.df = p->ptrs.dont_fragment();
+    fpk.tcp_window = p->ptrs.tcph->win();
+
+    fpk.mss = get_tcp_option(p, tcp::TcpOptCode::MAXSEG, fpk.mss_pos);
+    if (fpk.mss_pos >= 0)
+    {
+        get_tcp_option(p, tcp::TcpOptCode::SACKOK, fpk.sackok_pos);
+        fpk.ws = get_tcp_option(p, tcp::TcpOptCode::WSCALE, fpk.ws_pos);
+        get_tcp_option(p, tcp::TcpOptCode::TIMESTAMP, fpk.timestamp_pos);
+        mssOptionPresent = 1;
+    }
+
+    TCP_FP_MODE traffic_source = p->ptrs.tcph->is_ack() ? TCP_FP_MODE::SERVER : TCP_FP_MODE::CLIENT;
+
+    if (traffic_source == TCP_FP_MODE::SERVER)
+    {
+        if (!flowp)
+            return nullptr;
+
+        if (!mssOptionPresent)
+            return nullptr;
+
+        fpk.synmss = flowp->state.initial_mss;
+        fpk.num_syn_tcpopts = flowp->state.numopts;
+        fpk.syn_tcpopts = flowp->state.tcpopts;
+        fpk.syn_timestamp = flowp->state.timestamp;
+    }
+    else
+    {
+        if (!mssOptionPresent)
+        {
+            //client becomes unknown when client does not sent MSS option
+            return nullptr;
+        }
+    }
+
+    /* run the search and return the result */
+    const TcpFingerprint* fp = get_tcp_fp(fpk, p->ptrs.ip_api.ttl(), traffic_source);
+
+    return fp;
+}
+
+}
+
+void RNAFlow::init()
+{
+    inspector_id = snort::FlowData::create_flow_data_id();
+}
+
+size_t RNAFlow::size_of()
+{
+    return sizeof(*this);
+}
+
+bool FpFingerprintState::set(const Packet* p)
+{
+    int pos = 0;
+    numopts = 0;
+    initial_mss = get_tcp_option(p, tcp::TcpOptCode::MAXSEG, pos);
+    tcpopts[numopts++] = (uint8_t) tcp::TcpOptCode::MAXSEG;
+
+    get_tcp_option(p, tcp::TcpOptCode::SACKOK, pos);
+    if (pos >= 0)
+        tcpopts[numopts++] = (uint8_t) tcp::TcpOptCode::SACKOK;
+
+    get_tcp_option(p, tcp::TcpOptCode::WSCALE, pos);
+    if (pos >= 0)
+        tcpopts[numopts++] = (uint8_t) tcp::TcpOptCode::WSCALE;
+
+    timestamp = get_tcp_option(p, tcp::TcpOptCode::TIMESTAMP, pos);
+    if (pos >= 0)
+        tcpopts[numopts++] = (uint8_t) tcp::TcpOptCode::TIMESTAMP;
+    else
+        timestamp = -1;
+    timeout = p->pkth->ts.tv_sec;
+
+    return true;
 }
 
+
 #ifdef UNIT_TEST
-TEST_CASE("get_tcp_fp_processor", "[tcp_processor]")
+
+TEST_CASE("get_tcp_fp_processor", "[rna_fingerprint_tcp]")
+{
+    TcpFpProcessor* tfp = get_tcp_fp_processor();
+    CHECK(tfp == tcp_fp_processor);
+}
+
+TEST_CASE("clear_fingerprint", "[rna_fingerprint_tcp]")
+{
+    TcpFingerprint fpx;
+    uuid_clear(fpx.fpuuid);
+
+    RawFingerprint rawfp;
+    rawfp.fpid = 948;
+    rawfp.fp_type = 1;
+    rawfp.fpuuid = "12345678-1234-1234-1234-123456789012";
+    rawfp.ttl = 64;
+    rawfp.tcp_window = "10 20 30-40 50 60-70";
+    rawfp.mss = "X";
+    rawfp.id = "X";
+    rawfp.topts = "2 3 4 8";
+    rawfp.ws = "6";
+    rawfp.df = 1;
+
+    TcpFingerprint tfp(rawfp);
+    tfp.clear();
+    CHECK(tfp == fpx);
+}
+
+TEST_CASE("parse_fp_element", "[rna_fingerprint_tcp]")
+{
+    vector<string> str_elements = {"10", "20", "30-40", "50", "60-70"};
+    vector<FpElement> vfpe;
+    string str;
+    for (const auto& tok : str_elements)
+    {
+        str += tok + " ";
+        vfpe.emplace_back(FpElement(tok));
+    }
+
+    vector<FpElement> vfpe_test;
+    parse_fp_element(str, vfpe_test);
+    CHECK( equal(vfpe.begin(), vfpe.end(), vfpe_test.begin()) );
+}
+
+TEST_CASE("raw_to_tcp_fp", "[rna_fingerprint_tcp]")
+{
+    RawFingerprint rawfp;
+    rawfp.fpid = 948;
+    rawfp.fp_type = 1;
+    rawfp.fpuuid = "12345678-1234-1234-1234-123456789012";
+    rawfp.ttl = 64;
+    rawfp.tcp_window = "10 20 30-40 50 60-70";
+    rawfp.mss = "X";
+    rawfp.id = "X";
+    rawfp.topts = "2 3 4 8";
+    rawfp.ws = "6";
+    rawfp.df = 1;
+
+    TcpFingerprint tfpe;
+    tfpe.fpid = rawfp.fpid;
+    tfpe.fp_type = rawfp.fp_type;
+    uuid_parse(rawfp.fpuuid.c_str(), tfpe.fpuuid);
+    tfpe.ttl = rawfp.ttl;
+    tfpe.tcp_window = vector<FpElement> {
+        FpElement("10"), FpElement("20"), FpElement("30-40"),
+        FpElement("50"), FpElement("60-70") };
+    tfpe.mss = vector<FpElement> { FpElement("X") };
+    tfpe.id = vector<FpElement> { FpElement("X") };
+    tfpe.topts = vector<FpElement> {
+        FpElement("2"), FpElement("3"), FpElement("4"), FpElement("8") };
+    tfpe.ws.emplace_back(FpElement("6"));
+    tfpe.df = rawfp.df;
+
+    TcpFingerprint tfp_test(rawfp);
+
+    CHECK(tfpe == tfp_test);
+}
+
+TEST_CASE("get_tcp_fp", "[rna_fingerprint_tcp]")
 {
-    vector<FpTcpFingerprint> fplist;
-    tcp_fp_processor.push(fplist, TcpFpProcessor::TCP_FP_MODE::SERVER);
+    set_tcp_fp_processor(new TcpFpProcessor);
+    TcpFpProcessor* processor = get_tcp_fp_processor();
+
+    // Push some fingerprints to the processor:
+    RawFingerprint rawfp;
+    rawfp.fpid = 948;
+    rawfp.fp_type = 1;
+    rawfp.fpuuid = "12345678-1234-1234-1234-123456789012";
+    rawfp.ttl = 64;
+    rawfp.tcp_window = "-1";
+    rawfp.mss = "X";
+    rawfp.id = "X";
+    rawfp.topts = "2 3 4 8";
+    rawfp.ws = "6";
+    rawfp.df = 1;
+    processor->push(rawfp);
+    TcpFingerprint f948(rawfp);
+
+    rawfp.fpid = 30962;
+    rawfp.fp_type = 2;
+    rawfp.fpuuid = "12345678-1234-1234-1234-123456789013";
+    rawfp.ttl = 64;
+    rawfp.tcp_window = "-1";
+    rawfp.mss = "X";
+    rawfp.id = "X";
+    rawfp.topts = "2 4 8 3";
+    rawfp.ws = "8";
+    rawfp.df = 1;
+    processor->push(rawfp);
+    TcpFingerprint f30962(rawfp);
+
+    rawfp.fpid = 110005;
+    rawfp.fp_type = 10;
+    rawfp.fpuuid = "12345678-1234-1234-1234-123456789014";
+    rawfp.ttl = 64;
+    rawfp.tcp_window = "5712-5760";
+    rawfp.mss = "X";
+    rawfp.id = "X";
+    rawfp.topts = "2 4 8 3";
+    rawfp.ws = "7";
+    rawfp.df = 0;
+    processor->push(rawfp);
+    TcpFingerprint f110005(rawfp);
+
+    rawfp.fpid = 120001;
+    rawfp.fp_type = 11;
+    rawfp.fpuuid = "12345678-1234-1234-1234-123456789015";
+    rawfp.ttl = 64;
+    rawfp.tcp_window = "14400";
+    rawfp.mss = "X";
+    rawfp.id = "X";
+    rawfp.topts = "2 4 8 3";
+    rawfp.ws = "7";
+    rawfp.df = 0;
+    processor->push(rawfp);
+    TcpFingerprint f120001(rawfp);
+
+    rawfp.fpid = 2;
+    rawfp.fp_type = 1;
+    rawfp.fpuuid = "12345678-1234-1234-1234-123456789016";
+    rawfp.ttl = 64;
+    rawfp.tcp_window = "2144 5040-5840";
+    rawfp.mss = "X";
+    rawfp.id = "X";
+    rawfp.topts = "2";
+    rawfp.ws = "0-1";
+    rawfp.df = 0;
+    processor->push(rawfp);
+    TcpFingerprint f2(rawfp);
+
+    processor->make_tcp_fp_tables(TcpFpProcessor::TCP_FP_MODE::SERVER);
+    processor->make_tcp_fp_tables(TcpFpProcessor::TCP_FP_MODE::CLIENT);
+
+    // match time
+    const TcpFingerprint* tfp;
+    uint8_t ttl = 64;
+    TcpFpProcessor::TCP_FP_MODE mode = TcpFpProcessor::TCP_FP_MODE::SERVER;
+
+    // key that matches f_110005 - server side
+    uint8_t syn_tcpopts[] = {0, 0, 0, 0};
+    FpTcpKey key;
+    key.synmss = 1;
+    key.num_syn_tcpopts = 4;
+    key.syn_tcpopts = syn_tcpopts;
+    key.syn_timestamp = 1;
+    key.tcp_window = 5750;
+    key.mss = 4;
+    key.ws = 7;
+    key.mss_pos = 0;           // MAXSEG = 2 in position 0
+    key.sackok_pos = 1;        // SACKOK = 4 in position 1
+    key.timestamp_pos = 2;     // TIMESTAMP = 8 in position 2
+    key.ws_pos = 3;            // WSCALE = 3 in position 3
+    key.df = 0;
+    key.isIpv6 = 1;
+    syn_tcpopts[key.mss_pos] = (uint8_t) tcp::TcpOptCode::MAXSEG;
+    syn_tcpopts[key.timestamp_pos] = (uint8_t) tcp::TcpOptCode::TIMESTAMP;
+    syn_tcpopts[key.sackok_pos] = (uint8_t) tcp::TcpOptCode::SACKOK;
+    syn_tcpopts[key.ws_pos] = (uint8_t) tcp::TcpOptCode::WSCALE;
+
+    tfp = processor->get_tcp_fp(key, ttl, mode);
+    CHECK( (tfp && *tfp == f110005) );
+
+    // as above, except don't set timestamp option 2 4 8 3
+    key.syn_timestamp = 0;
+    key.mss_pos = 0;           // MAXSEG = 2 in position 0
+    key.sackok_pos = 1;        // SACKOK = 4 in position 1
+    key.ws_pos = 2;            // WSCALE = 3 in position 2
+    key.num_syn_tcpopts = 3;
+    syn_tcpopts[key.mss_pos] = (uint8_t) tcp::TcpOptCode::MAXSEG;
+    syn_tcpopts[key.sackok_pos] = (uint8_t) tcp::TcpOptCode::SACKOK;
+    syn_tcpopts[key.ws_pos] = (uint8_t) tcp::TcpOptCode::WSCALE;
+
+    tfp = processor->get_tcp_fp(key, ttl, mode);
+    CHECK( (tfp && *tfp == f110005) );
+
+    // now match something on the client side
+    mode = TcpFpProcessor::TCP_FP_MODE::CLIENT;
+
+    // match f_30962 - client side
+    key.synmss = 1;
+    key.num_syn_tcpopts = 4;
+    key.syn_tcpopts = syn_tcpopts;
+    key.syn_timestamp = 1;
+    key.tcp_window = 1;        // fp tcp_window = -1 gets interpreted as 0-1
+    key.mss = 4;
+    key.ws = 8;
+    key.mss_pos = 0;           // MAXSEG = 2 in position 0
+    key.sackok_pos = 1;        // SACKOK = 4 in position 1
+    key.timestamp_pos = 2;     // TIMESTAMP = 8 in position 2
+    key.ws_pos = 3;            // WSCALE = 3 in position 3
+    key.df = 1;
+    key.isIpv6 = 0;
+    syn_tcpopts[key.mss_pos] = (uint8_t) tcp::TcpOptCode::MAXSEG;
+    syn_tcpopts[key.timestamp_pos] = (uint8_t) tcp::TcpOptCode::TIMESTAMP;
+    syn_tcpopts[key.sackok_pos] = (uint8_t) tcp::TcpOptCode::SACKOK;
+    syn_tcpopts[key.ws_pos] = (uint8_t) tcp::TcpOptCode::WSCALE;
+
+    tfp = processor->get_tcp_fp(key, ttl, mode);
+    CHECK( (tfp && *tfp == f30962) );
+
+    // again, with no ws
+    key.ws_pos = -1;
+    key.num_syn_tcpopts = 3;
+    tfp = processor->get_tcp_fp(key, ttl, mode);
+    CHECK(tfp == nullptr);
 
-    snort::TcpFpProcessor* tfp = snort::get_tcp_fp_processor();
-    CHECK(tfp == &tcp_fp_processor);
+    delete processor;
+    set_tcp_fp_processor(nullptr);
 }
 
 #endif
index 948996e4693f951333522309074c440453b232bc..4331c00361b5dc1b06dbf1539bebf4d2c2ddb806 100644 (file)
 #ifndef RNA_FINGERPRINT_TCP_H
 #define RNA_FINGERPRINT_TCP_H
 
-#include <list>
+#include <unordered_map>
 #include <vector>
 
 #include "main/snort_types.h"
 #include "protocols/packet.h"
+#include "protocols/tcp.h"
 
 #include "rna_fingerprint.h"
 
+class RNAFlow;
+
 namespace snort
 {
 
-enum FpElementType
-{
-    RANGE=1,
-    INCREMENT,
-    SYN_MATCH,
-    RANDOM,
-    DONT_CARE,
-    SYNTS
-};
-
-class FpElement
+class SO_PUBLIC TcpFingerprint : public FpFingerprint
 {
 public:
-    FpElementType type;
-    union
-    {
-        int value;
-        struct
-        {
-            int min;
-            int max;
-        } range;
-    } d;
-};
 
-class FpTcpFingerprint : public FpFingerprint
-{
-public:
+    TcpFingerprint() = default;
+    TcpFingerprint(const RawFingerprint& rfp);
 
     std::vector<FpElement> tcp_window;
     std::vector<FpElement> mss;
     std::vector<FpElement> id;
     std::vector<FpElement> topts;
     std::vector<FpElement> ws;
+    bool df = false;
+
+    void clear() override
+    {
+        FpFingerprint::clear();
+        tcp_window.clear();
+        mss.clear();
+        id.clear();
+        topts.clear();
+        ws.clear();
+        df = false;
+    }
+
+    bool operator==(const TcpFingerprint& y) const;
+};
+
+struct FpTcpKey
+{
+    int synmss;
+    uint8_t *syn_tcpopts;
+    int num_syn_tcpopts;
+    int syn_timestamp;
+
+    int tcp_window;
+    int mss;
+    int ws;
+
+    int mss_pos;
+    int ws_pos;
+    int sackok_pos;
+    int timestamp_pos;
+
     char df;
+    uint8_t isIpv6;
 };
 
-class TcpFpProcessor
+class SO_PUBLIC TcpFpProcessor
 {
 public:
 
+    typedef std::unordered_map<uint32_t, TcpFingerprint> TcpFpContainer;
+
     enum TCP_FP_MODE { SERVER, CLIENT };
 
-    typedef std::list<snort::FpTcpFingerprint>::iterator Iter_t;
+    void push(const TcpFingerprint& tfp);
+
+    void make_tcp_fp_tables(TCP_FP_MODE mode);
 
-    SO_PUBLIC void push(const std::vector<snort::FpTcpFingerprint>&, TCP_FP_MODE);
+    const TcpFingerprint* get_tcp_fp(const FpTcpKey& key, uint8_t ttl, TCP_FP_MODE mode) const;
 
+    const TcpFingerprint* get(const Packet* p, RNAFlow* flowp) const;
+
+    const TcpFingerprint* get(uint32_t fpid) const
+    {
+        auto it = tcp_fps.find(fpid);
+        return it != tcp_fps.end() ? &it->second : nullptr;
+    }
+
+    const TcpFpContainer& get_tcp_fps() const
+    { return tcp_fps; }
 
 private:
 
-    // table_tcp_xxx[i] contains all fingerprints whose tcp window range
-    // contains i
-    std::vector<const snort::FpTcpFingerprint*> table_tcp_server[snort::MAX_PORTS];
-    std::vector<const snort::FpTcpFingerprint*> table_tcp_client[snort::MAX_PORTS];
+    // underlying container for input fingerprints
+    TcpFpContainer tcp_fps;
+
+    // table_tcp_xxx[i] contains pointers into tcp_fps to all fingerprints
+    // whose tcp window range contains i
+    static constexpr uint32_t table_size = TCP_MAXWIN + 1;
+    std::vector<const snort::TcpFingerprint*> table_tcp_server[table_size];
+    std::vector<const snort::TcpFingerprint*> table_tcp_client[table_size];
 };
 
-SO_PUBLIC TcpFpProcessor* get_tcp_fp_processor();
 }
 
+snort::TcpFpProcessor* get_tcp_fp_processor();
+void set_tcp_fp_processor(snort::TcpFpProcessor*);
+
+struct FpFingerprintState
+{
+    int initial_mss = -1;
+    int timestamp = -1;
+    int numopts = -1;
+    uint8_t tcpopts[4];
+    time_t timeout = -1;
+
+    bool set(const snort::Packet*);
+};
+
+class RNAFlow : public snort::FlowData
+{
+public:
+    FpFingerprintState state;
+
+
+    RNAFlow() : FlowData(inspector_id) { }
+    ~RNAFlow() override { }
+
+    static void init();
+    size_t size_of() override;
+
+    static unsigned inspector_id;
+};
+
 #endif
diff --git a/src/network_inspectors/rna/rna_fp_reader.h b/src/network_inspectors/rna/rna_fp_reader.h
deleted file mode 100644 (file)
index a39947d..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-//--------------------------------------------------------------------------
-// Copyright (C) 2020-2020 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.
-//--------------------------------------------------------------------------
-
-// rna_fp_reader.h author Silviu Minut <sminut@cisco.com>
-
-#ifndef RNA_FP_READER_H
-#define RNA_FP_READER_H
-
-#include <vector>
-
-#include "main/snort_types.h"
-
-#include "rna_fingerprint_tcp.h"
-
-namespace snort
-{
-
-class RnaFingerprintReader
-{
-public:
-    RnaFingerprintReader() { }
-    virtual ~RnaFingerprintReader() { }
-    virtual bool init(const char*) { return true; }
-
-    const std::vector<FpTcpFingerprint>& get_tcp_server_fps() const { return tcp_server_fps; }
-    const std::vector<FpTcpFingerprint>& get_tcp_client_fps() const { return tcp_client_fps; }
-
-protected:
-    std::vector<FpTcpFingerprint> tcp_server_fps;
-    std::vector<FpTcpFingerprint> tcp_client_fps;
-};
-
-SO_PUBLIC const RnaFingerprintReader* get_rna_fp_reader();
-SO_PUBLIC void set_rna_fp_reader(RnaFingerprintReader*);
-
-}
-
-#endif
index e1ddfe4c04d97f90a8dd6f5519d533ce4c3c407e..bf4daca61f9c692d9ee976d6165316aa4cc534bf 100644 (file)
@@ -34,6 +34,9 @@
 #include "protocols/packet.h"
 
 #include "rna_event_handler.h"
+#include "rna_fingerprint_tcp.h"
+#include "rna_module.h"
+#include "rna_pnd.h"
 
 #ifdef UNIT_TEST
 #include "catch/snort_catch.h"
@@ -64,6 +67,8 @@ RnaInspector::~RnaInspector()
 {
     delete pnd;
     delete rna_conf;
+    if (mod_conf)
+        delete mod_conf->processor;
     delete mod_conf;
 }
 
@@ -104,7 +109,6 @@ void RnaInspector::show(const SnortConfig*) const
     if ( mod_conf )
     {
         ConfigLogger::log_value("rna_conf_path", mod_conf->rna_conf_path.c_str());
-        ConfigLogger::log_value("fingerprint_dir", mod_conf->fingerprint_dir.c_str());
         ConfigLogger::log_flag("enable_logger", mod_conf->enable_logger);
         ConfigLogger::log_flag("log_when_idle", mod_conf->log_when_idle);
     }
@@ -123,6 +127,7 @@ void RnaInspector::show(const SnortConfig*) const
 void RnaInspector::tinit()
 {
     // thread local initialization
+    set_tcp_fp_processor(mod_conf->processor);
 }
 
 void RnaInspector::tterm()
@@ -183,6 +188,18 @@ void RnaInspector::load_rna_conf()
     in_stream.close();
 }
 
+TcpFpProcessor* RnaInspector::get_or_create_fp_processor()
+{
+    if (mod_conf)
+    {
+        if (!mod_conf->processor)
+            mod_conf->processor = new TcpFpProcessor;
+        return mod_conf->processor;
+    }
+    return nullptr;
+}
+
+
 //-------------------------------------------------------------------------
 // api stuff
 //-------------------------------------------------------------------------
@@ -196,6 +213,7 @@ static void rna_mod_dtor(Module* m)
 static void rna_inspector_pinit()
 {
     // global initialization
+    RNAFlow::init();
 }
 
 static void rna_inspector_pterm()
index 69b45f09271ebc9f971a816b98b7083003a9f26d..44658593d5cb409c292c3a40f63bbe7ac9aa33a1 100644 (file)
 
 #include "framework/inspector.h"
 
-#include "rna_module.h"
-#include "rna_pnd.h"
+#include <string>
 
 namespace snort
 {
 struct Packet;
+class TcpFpProcessor;
 }
 
-class RnaInspector : public snort::Inspector
+struct RnaConfig;
+class RnaModule;
+struct RnaModuleConfig;
+class RnaPnd;
+
+class SO_PUBLIC RnaInspector : public snort::Inspector
 {
 public:
     RnaInspector(RnaModule*);
@@ -43,12 +48,13 @@ public:
     void tinit() override;
     void tterm() override;
 
+    snort::TcpFpProcessor* get_or_create_fp_processor();
+
 private:
     void load_rna_conf();
-    const RnaModuleConfig* mod_conf = nullptr;
+    RnaModuleConfig* mod_conf = nullptr;
     RnaConfig* rna_conf = nullptr;
     RnaPnd* pnd = nullptr;
 };
 
 #endif
-
index 8c5e6272e9ab5fda55a851b222529ea99fcd71b8..95a42120f83d2d6d451874c83e7f121d75e591dd 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "managers/event_manager.h"
 #include "protocols/packet.h"
+#include "rna_fingerprint_tcp.h"
 #include "rna_logger_common.h"
 
 #ifdef UNIT_TEST
@@ -44,13 +45,14 @@ using namespace snort;
 
 bool RnaLogger::log(uint16_t type, uint16_t subtype, const Packet* p, RnaTracker* ht,
     const struct in6_addr* src_ip, const uint8_t* src_mac, uint32_t event_time,
-    void* cond_var, const HostMac* hm, const uint16_t proto)
+    void* cond_var, const HostMac* hm, const uint16_t proto,
+    const TcpFingerprint* tfp)
 {
     if ( !enabled )
         return false;
 
     assert(ht);
-    RnaLoggerEvent rle(type, subtype, ht, src_mac, hm, proto);
+    RnaLoggerEvent rle(type, subtype, ht, src_mac, hm, proto, tfp);
     if ( src_ip and (!IN6_IS_ADDR_V4MAPPED(src_ip) or src_ip->s6_addr32[3]) )
         rle.ip = src_ip;
 
index c9d87da4fa08b677c68b9c28b14d497b91665a7d..99430afd6774126cb4571cfef5407dadf6afe981 100644 (file)
@@ -28,6 +28,7 @@ namespace snort
 {
 class Flow;
 struct Packet;
+class TcpFingerprint;
 }
 
 using RnaTracker = std::shared_ptr<snort::HostTracker>;
@@ -35,8 +36,9 @@ using RnaTracker = std::shared_ptr<snort::HostTracker>;
 struct RnaLoggerEvent : public Event
 {
     RnaLoggerEvent(uint16_t p_type, uint16_t p_subtype, const RnaTracker* p_ht,
-        const uint8_t* p_mac, const snort::HostMac* p_hm, const uint16_t p_proto)
-        : type(p_type), subtype(p_subtype), ht(p_ht), mac(p_mac), hm(p_hm), proto(p_proto) { }
+        const uint8_t* p_mac, const snort::HostMac* p_hm, const uint16_t p_proto,
+        const snort::TcpFingerprint* tcp_fp)
+        : type(p_type), subtype(p_subtype), ht(p_ht), mac(p_mac), hm(p_hm), proto(p_proto), tfp(tcp_fp) { }
     uint16_t type;
     uint16_t subtype;
     const RnaTracker* ht;
@@ -45,6 +47,7 @@ struct RnaLoggerEvent : public Event
     void* cond_var = nullptr;
     const snort::HostMac* hm;
     const uint16_t proto;
+    const snort::TcpFingerprint* tfp = nullptr;
 };
 
 class RnaLogger
@@ -54,7 +57,8 @@ public:
     bool log(uint16_t type, uint16_t subtype, const snort::Packet* p, RnaTracker* ht,
        const struct in6_addr* src_ip, const uint8_t* src_mac,
        uint32_t event_time = 0, void* cond_var = nullptr,
-       const snort::HostMac* hm = nullptr, const uint16_t proto = 0);
+            const snort::HostMac* hm = nullptr, const uint16_t proto = 0,
+            const snort::TcpFingerprint* tfp = nullptr);
 
 private:
     const bool enabled;
index 69d3ec11f25f35779a2b5e9ba713e820a1d24d94..99bce39f51e71e17b221dfe895920e524f2c9694 100644 (file)
@@ -25,6 +25,7 @@
     #define NEW_HOST            1
     #define NEW_NET_PROTOCOL    3
     #define NEW_XPORT_PROTOCOL  4
+    #define NEW_OS              8
 
 #define RNA_EVENT_CHANGE    1001
     #define CHANGE_HOPS                 5
index a33e7a1733c5aff3ed6099f1c359c24cebb45366..3806c262b266ba75c3c6b54c01d6d6df286c5b0f 100644 (file)
@@ -39,6 +39,7 @@
 #include "src/main.h"
 #include "utils/util.h"
 
+#include "rna_fingerprint_tcp.h"
 #include "rna_mac_cache.h"
 
 #ifdef UNIT_TEST
@@ -114,14 +115,46 @@ static const Command rna_cmds[] =
     { nullptr, nullptr, nullptr, nullptr }
 };
 
+static const Parameter rna_fp_params[] =
+{
+    { "fpid", Parameter::PT_INT, "0:max32", "0",
+      "fingerprint id" },
+
+    { "type", Parameter::PT_INT, "0:max32", "0",
+      "fingerprint type" },
+
+    { "uuid", Parameter::PT_STRING, nullptr, nullptr,
+      "fingerprint uuid" },
+
+    { "ttl", Parameter::PT_INT, "0:256", "0",
+      "fingerprint ttl" },
+
+    { "tcp_window", Parameter::PT_STRING, nullptr, nullptr,
+      "fingerprint tcp window" },
+
+    { "mss", Parameter::PT_STRING, nullptr, "X",
+      "fingerprint mss" },
+
+    { "id", Parameter::PT_STRING, nullptr, "X",
+      "id" },
+
+    { "topts", Parameter::PT_STRING, nullptr, nullptr,
+      "fingerprint tcp options" },
+
+    { "ws", Parameter::PT_STRING, nullptr, "X",
+      "fingerprint window size" },
+
+    { "df", Parameter::PT_BOOL, nullptr, "false",
+      "fingerprint don't fragment flag" },
+
+    { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
 static const Parameter rna_params[] =
 {
     { "rna_conf_path", Parameter::PT_STRING, nullptr, nullptr,
       "path to rna configuration" },
 
-    { "fingerprint_dir", Parameter::PT_STRING, nullptr, nullptr,
-      "directory to fingerprint patterns" },
-
     { "enable_logger", Parameter::PT_BOOL, nullptr, "true",
       "enable or disable writing discovery events into logger" },
 
@@ -131,6 +164,9 @@ static const Parameter rna_params[] =
     { "dump_file", Parameter::PT_STRING, nullptr, nullptr,
       "file name to dump RNA mac cache on shutdown; won't dump by default" },
 
+    { "tcp_fingerprints", Parameter::PT_LIST, rna_fp_params, nullptr,
+      "list tcp fingerprints" },
+
     { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
 };
 
@@ -170,19 +206,26 @@ RnaModule::~RnaModule()
 
 bool RnaModule::begin(const char* fqn, int, SnortConfig*)
 {
-    if (strcmp(fqn, RNA_NAME))
+    if (!is_valid_fqn(fqn))
         return false;
-    else if (!mod_conf)
+
+    if (!mod_conf)
         mod_conf = new RnaModuleConfig;
+
+    if (!strcmp(fqn, "rna.tcp_fingerprints"))
+    {
+        fingerprint.clear();
+        if (!mod_conf->processor)
+            mod_conf->processor = new TcpFpProcessor;
+    }
+
     return true;
 }
 
-bool RnaModule::set(const char*, Value& v, SnortConfig*)
+bool RnaModule::set(const char* fqn, Value& v, SnortConfig*)
 {
     if (v.is("rna_conf_path"))
         mod_conf->rna_conf_path = string(v.get_string());
-    else if (v.is("fingerprint_dir"))
-        mod_conf->fingerprint_dir = string(v.get_string());
     else if (v.is("enable_logger"))
         mod_conf->enable_logger = v.get_bool();
     else if (v.is("log_when_idle"))
@@ -193,23 +236,65 @@ bool RnaModule::set(const char*, Value& v, SnortConfig*)
             snort_free((void*)dump_file);
         dump_file = snort_strdup(v.get_string());
     }
+
+    else if (fqn && strstr(fqn, "rna.tcp_fingerprints"))
+    {
+        if (v.is("fpid"))
+            fingerprint.fpid = v.get_uint32();
+        else if (v.is("type"))
+            fingerprint.fp_type = v.get_uint32();
+        else if (v.is("uuid"))
+            fingerprint.fpuuid = v.get_string();
+        else if (v.is("ttl"))
+            fingerprint.ttl = v.get_uint8();
+        else if (v.is("tcp_window"))
+            fingerprint.tcp_window = v.get_string();
+        else if (v.is("mss"))
+            fingerprint.mss = v.get_string();
+        else if (v.is("id"))
+            fingerprint.id = v.get_string();
+        else if (v.is("topts"))
+            fingerprint.topts = v.get_string();
+        else if (v.is("ws"))
+            fingerprint.ws = v.get_string();
+        else if (v.is("df"))
+            fingerprint.df = v.get_uint8();
+    }
+
     else
         return false;
 
     return true;
 }
 
-bool RnaModule::end(const char* fqn, int, SnortConfig* sc)
+bool RnaModule::end(const char* fqn, int index, SnortConfig* sc)
 {
-    if ( mod_conf == nullptr and strcmp(fqn, RNA_NAME) == 0 )
+    if ( mod_conf == nullptr || !is_valid_fqn(fqn) )
         return false;
 
-    sc->set_run_flags(RUN_FLAG__TRACK_ON_SYN); // Internal flag to track TCP on SYN
+    if ( !strcmp(fqn, RNA_NAME) )
+    {
+        sc->set_run_flags(RUN_FLAG__TRACK_ON_SYN); // Internal flag to track TCP on SYN
+
+        if ( sc->ip_frags_only() )
+        {
+            WarningMessage("RNA: Disabling stream.ip_frags_only option!\n");
+            sc->clear_run_flags(RUN_FLAG__IP_FRAGS_ONLY);
+        }
+
+        if (mod_conf->processor)
+        {
+            mod_conf->processor->make_tcp_fp_tables(TcpFpProcessor::TCP_FP_MODE::SERVER);
+            mod_conf->processor->make_tcp_fp_tables(TcpFpProcessor::TCP_FP_MODE::CLIENT);
+        }
+    }
 
-    if ( sc->ip_frags_only() )
+    if ( index > 0 && mod_conf->processor && !strcmp(fqn, "rna.tcp_fingerprints") )
     {
-        WarningMessage("RNA: Disabling stream.ip_frags_only option!\n");
-        sc->clear_run_flags(RUN_FLAG__IP_FRAGS_ONLY);
+        // there is an implicit conversion here from raw fingerprint (all
+        // strings) to tcp fingerprint, done by the tcp fingerprint constructor
+        mod_conf->processor->push(fingerprint);
+        fingerprint.clear();
     }
 
     return true;
@@ -274,7 +359,14 @@ bool RnaModule::log_mac_cache(const char* outfile)
     return 0;
 }
 
+bool RnaModule::is_valid_fqn(const char* fqn) const
+{
+    return !strcmp(fqn, RNA_NAME) || !strcmp(fqn, "rna.tcp_fingerprints");
+}
+
+
 #ifdef UNIT_TEST
+
 TEST_CASE("RNA module", "[rna_module]")
 {
     SECTION("module begin, set, end")
@@ -290,10 +382,6 @@ TEST_CASE("RNA module", "[rna_module]")
         v1.set(Parameter::find(rna_params, "rna_conf_path"));
         CHECK(mod.set(nullptr, v1, nullptr) == true);
 
-        Value v2("/dir/fingerprints");
-        v2.set(Parameter::find(rna_params, "fingerprint_dir"));
-        CHECK(mod.set(nullptr, v2, nullptr) == true);
-
         Value v3("dummy");
         CHECK(mod.set(nullptr, v3, nullptr) == false);
         CHECK(mod.end("rna", 0, &sc) == true);
@@ -301,7 +389,6 @@ TEST_CASE("RNA module", "[rna_module]")
         RnaModuleConfig* rc = mod.get_config();
         CHECK(rc != nullptr);
         CHECK(rc->rna_conf_path == "rna.conf");
-        CHECK(rc->fingerprint_dir == "/dir/fingerprints");
 
         delete rc;
     }
@@ -336,4 +423,5 @@ TEST_CASE("RNA module", "[rna_module]")
         delete mod.get_config();
     }
 }
+
 #endif
index f25c50affd30ea72f4d3b0228c620870c58bcfcf..0124bd6a2d05bc621ed262664ea1b1a887058223 100644 (file)
@@ -25,9 +25,8 @@
 #include "profiler/profiler.h"
 
 #include "rna_config.h"
-
-#define RNA_NAME "rna"
-#define RNA_HELP "Real-time network awareness and OS fingerprinting (experimental)"
+#include "rna_fingerprint.h"
+#include "rna_name.h"
 
 struct RnaStats
 {
@@ -71,6 +70,9 @@ private:
     RnaModuleConfig* mod_conf = nullptr;
     const char* dump_file = nullptr;
 
+    RawFingerprint fingerprint;
+
+    bool is_valid_fqn(const char* fqn) const;
 };
 
 #endif
similarity index 54%
rename from src/network_inspectors/rna/rna_fp_reader.cc
rename to src/network_inspectors/rna/rna_name.h
index 57ef652adde83332ea3406b21a0cb3509a6c0312..8fa075a002bdc7346c78b728111b99479d16e865 100644 (file)
@@ -1,5 +1,5 @@
 //--------------------------------------------------------------------------
-// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved.
+// Copyright (C) 2019-2020 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
 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 //--------------------------------------------------------------------------
 
-// rna_fp_reader.cc author Silviu Minut <sminut@cisco.com>
+// rna_name.h author Silviu Minut <sminut@cisco.com>
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "rna_fp_reader.h"
-
-#ifdef UNIT_TEST
-#include "catch/snort_catch.h"
-#endif
-
-using namespace snort;
-
-static RnaFingerprintReader* fp_reader = nullptr;
-
-namespace snort
-{
-
-const RnaFingerprintReader* get_rna_fp_reader()
-{
-    return fp_reader;
-}
-
-void set_rna_fp_reader(RnaFingerprintReader* fpr)
-{
-    fp_reader = fpr;
-}
-
-}
-
-#ifdef UNIT_TEST
-TEST_CASE("rna_fp_reader", "[rna_fp_reader]")
-{
-    RnaFingerprintReader rna_fp_reader;
-    snort::set_rna_fp_reader(&rna_fp_reader);
-    CHECK(fp_reader == &rna_fp_reader);
+#ifndef RNA_NAME_H
+#define RNA_NAME_H
 
-    const RnaFingerprintReader* fpr = snort::get_rna_fp_reader();
-    CHECK(fp_reader == fpr);
-}
+#define RNA_NAME "rna"
+#define RNA_HELP "Real-time network awareness and OS fingerprinting (experimental)"
 
 #endif
index 71bb68fd6fc475b2053397372130fe00b388903d..1b55812c58d8a4333cbfea682c1ac93aa862a0b6 100644 (file)
@@ -35,6 +35,7 @@
 #include "protocols/protocol_ids.h"
 #include "protocols/tcp.h"
 
+#include "rna_fingerprint_tcp.h"
 #include "rna_logger_common.h"
 
 #ifdef UNIT_TEST
@@ -105,11 +106,21 @@ void RnaPnd::analyze_flow_non_ip(const Packet* p)
 
 void RnaPnd::analyze_flow_tcp(const Packet* p, TcpPacketType type)
 {
-    // If and when flow stores rna state, process the flow data here before global cache access
-    if ( is_eligible_tcp(p) and filter.is_host_monitored(p) )
-        discover_network_tcp(p);
+    if ( is_eligible_tcp(p) )
+    {
+        // If it's a tcp SYN packet, create a fingerprint state for
+        // the SYN-ACK, but only if we're monitoring the destination (server)
+        const auto& dst_ip = p->ptrs.ip_api.get_dst();
+        if ( type == TcpPacketType::SYN && filter.is_host_monitored(p, nullptr, dst_ip) )
+        {
+            RNAFlow* rna_flow = new RNAFlow();
+            p->flow->set_flow_data(rna_flow);
+            rna_flow->state.set(p);
+        }
 
-    UNUSED(type);
+        if ( filter.is_host_monitored(p) )
+            discover_network_tcp(p);
+    }
 }
 
 void RnaPnd::analyze_flow_udp(const Packet* p)
@@ -203,6 +214,22 @@ void RnaPnd::discover_network(const Packet* p, uint8_t ttl)
     {
         generate_change_host_update(&ht, p, src_ip, src_mac, packet_time());
     }
+
+    // Fingerprint stuff
+    const TcpFpProcessor* processor;
+    if ( p->is_tcp() && (processor = get_tcp_fp_processor()) != nullptr )
+    {
+        RNAFlow* rna_flow = nullptr;
+        if ( p->ptrs.tcph->is_syn_ack() )
+            rna_flow = (RNAFlow*) p->flow->get_flow_data(RNAFlow::inspector_id);
+        const TcpFingerprint* tfp = processor->get(p, rna_flow);
+
+        if (tfp && ht->add_tcp_fingerprint(tfp->fpid))
+        {
+            logger.log(RNA_EVENT_NEW, NEW_OS, p, &ht, src_ip_ptr,
+                src_mac, 0, nullptr, nullptr, ptype, tfp);
+        }
+    }
 }
 
 inline void RnaPnd::update_vlan(const Packet* p, HostTrackerMac& hm)
index 697466db0343f8307c82c0ea6181f7c70a380c08..e087bfead25ddca15526e5c7b3ca4c4848c5a04c 100644 (file)
@@ -1,7 +1,9 @@
 add_cpputest( rna_module_test
     SOURCES
         ../../../framework/parameter.cc
+        ../rna_fingerprint.cc
         $<TARGET_OBJECTS:catch_tests>
     LIBS
         ${DNET_LIBRARIES}
+        uuid
 )
index 3043dc255607604696a7cd0df553c4cdd5582f53..74aa50520349988e16db9e7c93a6df1f9638100f 100644 (file)
@@ -62,6 +62,52 @@ void WarningMessage(const char*,...) {}
 SnortConfig::SnortConfig(SnortConfig const*) {}
 SnortConfig::~SnortConfig() {}
 time_t packet_time() { return 0; }
+
+// tcp fingerprint functions
+void TcpFpProcessor::push(const TcpFingerprint&) { }
+void TcpFpProcessor::make_tcp_fp_tables(TCP_FP_MODE) { }
+const TcpFingerprint* TcpFpProcessor::get_tcp_fp(const FpTcpKey&, uint8_t, TCP_FP_MODE) const
+{ return nullptr; }
+const TcpFingerprint* TcpFpProcessor::get(const Packet*, RNAFlow*) const
+{ return nullptr; }
+TcpFpProcessor* get_tcp_fp_processor() { return nullptr; }
+void set_tcp_fp_processor(TcpFpProcessor*) { }
+
+TcpFingerprint::TcpFingerprint(const RawFingerprint&) { }
+bool TcpFingerprint::operator==(const TcpFingerprint&) const { return true; }
+
+// inspector
+class RnaInspector
+{
+public:
+
+// The module gets created first, with a mod_conf and fingerprint processor,
+// then, when the module is done, we take ownership of that.
+RnaInspector(RnaModule* mod)
+{
+    mod_conf = mod->get_config();
+}
+
+~RnaInspector()
+{
+    if (mod_conf)
+    {
+       if (mod_conf->processor)
+           delete mod_conf->processor;
+       delete mod_conf;
+    }
+}
+
+TcpFpProcessor* get_fp_processor()
+{
+    return mod_conf->processor;
+}
+
+private:
+    RnaModuleConfig* mod_conf = nullptr;
+};
+
+
 } // end of namespace snort
 
 #endif
index 7b3fc109dab1e3e8d0ec353d07ccc506008b2be4..e41a146e3fa61d8bce6936593b3d247cb3606b30 100644 (file)
@@ -63,6 +63,200 @@ TEST(rna_module_test, reload_fingerprint)
     CHECK_FALSE(Swapper::get_reload_in_progress());
 }
 
+TEST(rna_module_test, push_tcp_fingerprints)
+{
+    // In plain English, we test that the RNA module pushes tcp fingerprints
+    // correctly to the processor:
+    // 1. create a raw fingerprint
+    // 2. create the corresponding expected tcp fingerprint
+    // 3. call Module::set() for each field
+    // 4. call Module::end(), to push the module internal fingerprint to the
+    //    config processor
+    // 5. do this for a client-type fingerprint and a server-type fingerprint
+    // 6. the module config is private, so create an inspector and pass the
+    //    config on to the inspector
+    // 6. retrieve the client and server vectors from the processors and
+    //    match expected in each case
+
+    RnaModule mod;
+
+    // input fingerprint
+    RawFingerprint rawfp;
+    rawfp.fpid = 948;
+    rawfp.fp_type = FpFingerprint::FpType::FINGERPRINT_TYPE_SERVER;
+    rawfp.fpuuid = "12345678-1234-1234-1234-012345678912";
+    rawfp.ttl = 64;
+    rawfp.tcp_window = "10 20 30-40 50 60-70";
+    rawfp.mss = "X";
+    rawfp.id = "X";
+    rawfp.topts = "2 3 4 8";
+    rawfp.ws = "6";
+    rawfp.df = 1;
+
+    // expected
+    TcpFingerprint tfpe;
+    tfpe.fpid = rawfp.fpid;
+    tfpe.fp_type = rawfp.fp_type;
+    uuid_parse(rawfp.fpuuid.c_str(), tfpe.fpuuid);
+    tfpe.ttl = rawfp.ttl;
+    tfpe.tcp_window = vector<FpElement> {
+        FpElement("10"), FpElement("20"), FpElement("30-40"),
+        FpElement("50"), FpElement("60-70") };
+    tfpe.mss = vector<FpElement> { FpElement("X") };
+    tfpe.id = vector<FpElement> { FpElement("X") };
+    tfpe.topts = vector<FpElement> {
+        FpElement("2"), FpElement("3"), FpElement("4"), FpElement("8") };
+    tfpe.ws.emplace_back(FpElement("6"));
+    tfpe.df = rawfp.df;
+
+    CHECK(mod.begin("rna", 0, nullptr) == true);
+    CHECK(mod.begin("rna.tcp_fingerprints", 0, nullptr) == true); // instantiates processor
+
+    auto server_fpid = rawfp.fpid;
+    {
+        Value v((double) server_fpid);
+        v.set(Parameter::find(rna_fp_params, "fpid"));
+        CHECK(mod.set("rna.tcp_fingerprints.fpid", v, nullptr) == true);
+    }
+
+    {
+        Value v((double) rawfp.fp_type);
+        v.set(Parameter::find(rna_fp_params, "type"));
+        CHECK(mod.set("rna.tcp_fingerprints.type", v, nullptr) == true);
+    }
+
+    {
+        Value v(rawfp.fpuuid.c_str());
+        v.set(Parameter::find(rna_fp_params, "uuid"));
+        CHECK(mod.set("rna.tcp_fingerprints.uuid", v, nullptr) == true);
+    }
+
+    {
+        Value v((double) rawfp.ttl);
+        v.set(Parameter::find(rna_fp_params, "ttl"));
+        CHECK(mod.set("rna.tcp_fingerprints.ttl", v, nullptr) == true);
+    }
+
+    {
+        Value v(rawfp.tcp_window.c_str());
+        v.set(Parameter::find(rna_fp_params, "tcp_window"));
+        CHECK(mod.set("rna.tcp_fingerprints.tcp_window", v, nullptr) == true);
+    }
+
+    {
+        Value v(rawfp.mss.c_str());
+        v.set(Parameter::find(rna_fp_params, "mss"));
+        CHECK(mod.set("rna.tcp_fingerprints.mss", v, nullptr) == true);
+    }
+
+    {
+        Value v(rawfp.id.c_str());
+        v.set(Parameter::find(rna_fp_params, "id"));
+        CHECK(mod.set("rna.tcp_fingerprints.id", v, nullptr) == true);
+    }
+
+    {
+        Value v(rawfp.topts.c_str());
+        v.set(Parameter::find(rna_fp_params, "topts"));
+        CHECK(mod.set("rna.tcp_fingerprints.topts", v, nullptr) == true);
+    }
+
+    {
+        Value v(rawfp.ws.c_str());
+        v.set(Parameter::find(rna_fp_params, "ws"));
+        CHECK(mod.set("rna.tcp_fingerprints.ws", v, nullptr) == true);
+    }
+
+    {
+        Value v((double) rawfp.df);
+        v.set(Parameter::find(rna_fp_params, "df"));
+        CHECK(mod.set("rna.tcp_fingerprints.df", v, nullptr) == true);
+    }
+
+    // push it to the processor
+    CHECK(mod.end("rna.tcp_fingerprints", 0, nullptr) == true);
+
+    // add one for the client too, by changing only the type and id
+    auto client_fpid = rawfp.fpid+1;     // non duplicate id
+    rawfp.fp_type = FpFingerprint::FpType::FINGERPRINT_TYPE_CLIENT;
+
+    {
+        Value v((double) client_fpid);
+        v.set(Parameter::find(rna_fp_params, "fpid"));
+        CHECK(mod.set("rna.tcp_fingerprints.fpid", v, nullptr) == true);
+    }
+
+    {
+        Value v((double) rawfp.fp_type);
+        v.set(Parameter::find(rna_fp_params, "type"));
+        CHECK(mod.set("rna.tcp_fingerprints.type", v, nullptr) == true);
+    }
+
+    {
+        Value v(rawfp.fpuuid.c_str());
+        v.set(Parameter::find(rna_fp_params, "uuid"));
+        CHECK(mod.set("rna.tcp_fingerprints.uuid", v, nullptr) == true);
+    }
+
+    {
+        Value v((double) rawfp.ttl);
+        v.set(Parameter::find(rna_fp_params, "ttl"));
+        CHECK(mod.set("rna.tcp_fingerprints.ttl", v, nullptr) == true);
+    }
+
+    {
+        Value v(rawfp.tcp_window.c_str());
+        v.set(Parameter::find(rna_fp_params, "tcp_window"));
+        CHECK(mod.set("rna.tcp_fingerprints.tcp_window", v, nullptr) == true);
+    }
+
+    {
+        Value v(rawfp.mss.c_str());
+        v.set(Parameter::find(rna_fp_params, "mss"));
+        CHECK(mod.set("rna.tcp_fingerprints.mss", v, nullptr) == true);
+    }
+
+    {
+        Value v(rawfp.id.c_str());
+        v.set(Parameter::find(rna_fp_params, "id"));
+        CHECK(mod.set("rna.tcp_fingerprints.id", v, nullptr) == true);
+    }
+
+    {
+        Value v(rawfp.topts.c_str());
+        v.set(Parameter::find(rna_fp_params, "topts"));
+        CHECK(mod.set("rna.tcp_fingerprints.topts", v, nullptr) == true);
+    }
+
+    {
+        Value v(rawfp.ws.c_str());
+        v.set(Parameter::find(rna_fp_params, "ws"));
+        CHECK(mod.set("rna.tcp_fingerprints.ws", v, nullptr) == true);
+    }
+
+    {
+        Value v((double) rawfp.df);
+        v.set(Parameter::find(rna_fp_params, "df"));
+        CHECK(mod.set("rna.tcp_fingerprints.df", v, nullptr) == true);
+    }
+
+    // push it to the processor
+    CHECK(mod.end("rna.tcp_fingerprints", 0, nullptr) == true);
+
+    // only now create the inspector
+    RnaInspector inspector(&mod);   // inspector owns the processor
+
+    // final check
+    const auto* processor = inspector.get_fp_processor();
+
+    auto tfps = processor->get(server_fpid);
+    auto tfpc = processor->get(client_fpid);
+
+    // test fingerprint equality - does not use ids
+    CHECK(*tfps == tfpe);
+    CHECK(*tfpc == tfpe);
+}
+
 int main(int argc, char** argv)
 {
     return CommandLineTestRunner::RunAllTests(argc, argv);