From 3c7acdcd0fb3bae859eedf0c1dbb05273cbfd72c Mon Sep 17 00:00:00 2001 From: "Masud Hasan (mashasan)" Date: Wed, 26 Aug 2020 18:07:25 +0000 Subject: [PATCH] Merge pull request #2353 in SNORT/snort3 from ~SMINUT/snort3:tcp_fp_io to master Squashed commit of the following: commit d8f33db11b1589cf65dddc77fde9cb428f747e7e Author: Silviu Minut Date: Tue Jul 28 18:48:55 2020 -0400 rna: tcp fingerprints configuration, storage, matching and event generation --- src/framework/decode_data.h | 3 + src/host_tracker/host_tracker.cc | 15 + src/host_tracker/host_tracker.h | 5 + src/network_inspectors/rna/CMakeLists.txt | 6 +- src/network_inspectors/rna/dev_notes.txt | 141 ++++ src/network_inspectors/rna/rna_config.h | 7 +- src/network_inspectors/rna/rna_fingerprint.cc | 198 ++++++ src/network_inspectors/rna/rna_fingerprint.h | 115 +++- .../rna/rna_fingerprint_tcp.cc | 650 +++++++++++++++++- .../rna/rna_fingerprint_tcp.h | 128 +++- src/network_inspectors/rna/rna_fp_reader.h | 53 -- src/network_inspectors/rna/rna_inspector.cc | 20 +- src/network_inspectors/rna/rna_inspector.h | 16 +- src/network_inspectors/rna/rna_logger.cc | 6 +- src/network_inspectors/rna/rna_logger.h | 10 +- .../rna/rna_logger_common.h | 1 + src/network_inspectors/rna/rna_module.cc | 126 +++- src/network_inspectors/rna/rna_module.h | 8 +- .../rna/{rna_fp_reader.cc => rna_name.h} | 46 +- src/network_inspectors/rna/rna_pnd.cc | 35 +- .../rna/test/CMakeLists.txt | 2 + .../rna/test/rna_module_mock.h | 46 ++ .../rna/test/rna_module_test.cc | 194 ++++++ 23 files changed, 1649 insertions(+), 182 deletions(-) create mode 100644 src/network_inspectors/rna/rna_fingerprint.cc delete mode 100644 src/network_inspectors/rna/rna_fp_reader.h rename src/network_inspectors/rna/{rna_fp_reader.cc => rna_name.h} (54%) diff --git a/src/framework/decode_data.h b/src/framework/decode_data.h index 968e9d2f2..e12560ab7 100644 --- a/src/framework/decode_data.h +++ b/src/framework/decode_data.h @@ -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 diff --git a/src/host_tracker/host_tracker.cc b/src/host_tracker/host_tracker.cc index fce17fc83..8f9463cbc 100644 --- a/src/host_tracker/host_tracker.cc +++ b/src/host_tracker/host_tracker.cc @@ -259,6 +259,13 @@ void HostTracker::remove_inferred_services() } } +bool HostTracker::add_tcp_fingerprint(uint32_t fpid) +{ + lock_guard 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 ? ", " : ""); + } } diff --git a/src/host_tracker/host_tracker.h b/src/host_tracker/host_tracker.h index 8ce467481..bc45d0a0e 100644 --- a/src/host_tracker/host_tracker.h +++ b/src/host_tracker/host_tracker.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #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> network_protos; std::vector> xport_protos; std::vector services; + std::set, HostCacheAllocIp> tcp_fpids; + bool vlan_tag_present = false; vlan::VlanTagHdr vlan_tag; HostType host_type; diff --git a/src/network_inspectors/rna/CMakeLists.txt b/src/network_inspectors/rna/CMakeLists.txt index 3ab0b7297..7408e79db 100644 --- a/src/network_inspectors/rna/CMakeLists.txt +++ b/src/network_inspectors/rna/CMakeLists.txt @@ -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 diff --git a/src/network_inspectors/rna/dev_notes.txt b/src/network_inspectors/rna/dev_notes.txt index 9530ac493..c575ccd2c 100644 --- a/src/network_inspectors/rna/dev_notes.txt +++ b/src/network_inspectors/rna/dev_notes.txt @@ -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 diff --git a/src/network_inspectors/rna/rna_config.h b/src/network_inspectors/rna/rna_config.h index f6063cba1..b9626f243 100644 --- a/src/network_inspectors/rna/rna_config.h +++ b/src/network_inspectors/rna/rna_config.h @@ -21,12 +21,17 @@ #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 index 000000000..6ca4c271a --- /dev/null +++ b/src/network_inspectors/rna/rna_fingerprint.cc @@ -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 + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "rna_fingerprint.h" + +#include +#include + +#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 + diff --git a/src/network_inspectors/rna/rna_fingerprint.h b/src/network_inspectors/rna/rna_fingerprint.h index fad62ece6..24424dbea 100644 --- a/src/network_inspectors/rna/rna_fingerprint.h +++ b/src/network_inspectors/rna/rna_fingerprint.h @@ -21,21 +21,130 @@ #ifndef RNA_FINGERPRINT_H #define RNA_FINGERPRINT_H +#include +#include #include +#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 diff --git a/src/network_inspectors/rna/rna_fingerprint_tcp.cc b/src/network_inspectors/rna/rna_fingerprint_tcp.cc index f26d1df3b..e6931db1a 100644 --- a/src/network_inspectors/rna/rna_fingerprint_tcp.cc +++ b/src/network_inspectors/rna/rna_fingerprint_tcp.cc @@ -24,30 +24,95 @@ #include "rna_fingerprint_tcp.h" +#include + #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& 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& fplist, TCP_FP_MODE mode) +void TcpFpProcessor::make_tcp_fp_tables(TCP_FP_MODE mode) { - vector* 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& 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* 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; itopts) + { + for (i=0; i= key.num_syn_tcpopts || key.syn_timestamp) + continue; // tfp + + fp_optpos = 0; + for (const auto& fpe_topts : tfp->topts) + { + for (i=0; itopts.begin(); + for (i = 0; itopts.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 str_elements = {"10", "20", "30-40", "50", "60-70"}; + vector vfpe; + string str; + for (const auto& tok : str_elements) + { + str += tok + " "; + vfpe.emplace_back(FpElement(tok)); + } + + vector 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("10"), FpElement("20"), FpElement("30-40"), + FpElement("50"), FpElement("60-70") }; + tfpe.mss = vector { FpElement("X") }; + tfpe.id = vector { FpElement("X") }; + tfpe.topts = vector { + 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 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 diff --git a/src/network_inspectors/rna/rna_fingerprint_tcp.h b/src/network_inspectors/rna/rna_fingerprint_tcp.h index 948996e46..4331c0036 100644 --- a/src/network_inspectors/rna/rna_fingerprint_tcp.h +++ b/src/network_inspectors/rna/rna_fingerprint_tcp.h @@ -21,74 +21,134 @@ #ifndef RNA_FINGERPRINT_TCP_H #define RNA_FINGERPRINT_TCP_H -#include +#include #include #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 tcp_window; std::vector mss; std::vector id; std::vector topts; std::vector 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 TcpFpContainer; + enum TCP_FP_MODE { SERVER, CLIENT }; - typedef std::list::iterator Iter_t; + void push(const TcpFingerprint& tfp); + + void make_tcp_fp_tables(TCP_FP_MODE mode); - SO_PUBLIC void push(const std::vector&, 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 table_tcp_server[snort::MAX_PORTS]; - std::vector 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 table_tcp_server[table_size]; + std::vector 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 index a39947d7d..000000000 --- a/src/network_inspectors/rna/rna_fp_reader.h +++ /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 - -#ifndef RNA_FP_READER_H -#define RNA_FP_READER_H - -#include - -#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& get_tcp_server_fps() const { return tcp_server_fps; } - const std::vector& get_tcp_client_fps() const { return tcp_client_fps; } - -protected: - std::vector tcp_server_fps; - std::vector tcp_client_fps; -}; - -SO_PUBLIC const RnaFingerprintReader* get_rna_fp_reader(); -SO_PUBLIC void set_rna_fp_reader(RnaFingerprintReader*); - -} - -#endif diff --git a/src/network_inspectors/rna/rna_inspector.cc b/src/network_inspectors/rna/rna_inspector.cc index e1ddfe4c0..bf4daca61 100644 --- a/src/network_inspectors/rna/rna_inspector.cc +++ b/src/network_inspectors/rna/rna_inspector.cc @@ -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() diff --git a/src/network_inspectors/rna/rna_inspector.h b/src/network_inspectors/rna/rna_inspector.h index 69b45f092..44658593d 100644 --- a/src/network_inspectors/rna/rna_inspector.h +++ b/src/network_inspectors/rna/rna_inspector.h @@ -23,15 +23,20 @@ #include "framework/inspector.h" -#include "rna_module.h" -#include "rna_pnd.h" +#include 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 - diff --git a/src/network_inspectors/rna/rna_logger.cc b/src/network_inspectors/rna/rna_logger.cc index 8c5e6272e..95a42120f 100644 --- a/src/network_inspectors/rna/rna_logger.cc +++ b/src/network_inspectors/rna/rna_logger.cc @@ -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; diff --git a/src/network_inspectors/rna/rna_logger.h b/src/network_inspectors/rna/rna_logger.h index c9d87da4f..99430afd6 100644 --- a/src/network_inspectors/rna/rna_logger.h +++ b/src/network_inspectors/rna/rna_logger.h @@ -28,6 +28,7 @@ namespace snort { class Flow; struct Packet; +class TcpFingerprint; } using RnaTracker = std::shared_ptr; @@ -35,8 +36,9 @@ using RnaTracker = std::shared_ptr; 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; diff --git a/src/network_inspectors/rna/rna_logger_common.h b/src/network_inspectors/rna/rna_logger_common.h index 69d3ec11f..99bce39f5 100644 --- a/src/network_inspectors/rna/rna_logger_common.h +++ b/src/network_inspectors/rna/rna_logger_common.h @@ -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 diff --git a/src/network_inspectors/rna/rna_module.cc b/src/network_inspectors/rna/rna_module.cc index a33e7a173..3806c262b 100644 --- a/src/network_inspectors/rna/rna_module.cc +++ b/src/network_inspectors/rna/rna_module.cc @@ -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 diff --git a/src/network_inspectors/rna/rna_module.h b/src/network_inspectors/rna/rna_module.h index f25c50aff..0124bd6a2 100644 --- a/src/network_inspectors/rna/rna_module.h +++ b/src/network_inspectors/rna/rna_module.h @@ -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 diff --git a/src/network_inspectors/rna/rna_fp_reader.cc b/src/network_inspectors/rna/rna_name.h similarity index 54% rename from src/network_inspectors/rna/rna_fp_reader.cc rename to src/network_inspectors/rna/rna_name.h index 57ef652ad..8fa075a00 100644 --- a/src/network_inspectors/rna/rna_fp_reader.cc +++ b/src/network_inspectors/rna/rna_name.h @@ -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 @@ -16,46 +16,12 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //-------------------------------------------------------------------------- -// rna_fp_reader.cc author Silviu Minut +// rna_name.h author Silviu Minut -#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 diff --git a/src/network_inspectors/rna/rna_pnd.cc b/src/network_inspectors/rna/rna_pnd.cc index 71bb68fd6..1b55812c5 100644 --- a/src/network_inspectors/rna/rna_pnd.cc +++ b/src/network_inspectors/rna/rna_pnd.cc @@ -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) diff --git a/src/network_inspectors/rna/test/CMakeLists.txt b/src/network_inspectors/rna/test/CMakeLists.txt index 697466db0..e087bfead 100644 --- a/src/network_inspectors/rna/test/CMakeLists.txt +++ b/src/network_inspectors/rna/test/CMakeLists.txt @@ -1,7 +1,9 @@ add_cpputest( rna_module_test SOURCES ../../../framework/parameter.cc + ../rna_fingerprint.cc $ LIBS ${DNET_LIBRARIES} + uuid ) diff --git a/src/network_inspectors/rna/test/rna_module_mock.h b/src/network_inspectors/rna/test/rna_module_mock.h index 3043dc255..74aa50520 100644 --- a/src/network_inspectors/rna/test/rna_module_mock.h +++ b/src/network_inspectors/rna/test/rna_module_mock.h @@ -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 diff --git a/src/network_inspectors/rna/test/rna_module_test.cc b/src/network_inspectors/rna/test/rna_module_test.cc index 7b3fc109d..e41a146e3 100644 --- a/src/network_inspectors/rna/test/rna_module_test.cc +++ b/src/network_inspectors/rna/test/rna_module_test.cc @@ -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("10"), FpElement("20"), FpElement("30-40"), + FpElement("50"), FpElement("60-70") }; + tfpe.mss = vector { FpElement("X") }; + tfpe.id = vector { FpElement("X") }; + tfpe.topts = vector { + 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); -- 2.47.3