]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2494 in SNORT/snort3 from ~MIALTIZE/snort3:binder_rework to master
authorMichael Altizer (mialtize) <mialtize@cisco.com>
Tue, 6 Oct 2020 20:54:26 +0000 (20:54 +0000)
committerMichael Altizer (mialtize) <mialtize@cisco.com>
Tue, 6 Oct 2020 20:54:26 +0000 (20:54 +0000)
Squashed commit of the following:

commit c7420f49c5918ac276b666c5740997b3cefe85fe
Author: Michael Altizer <mialtize@cisco.com>
Date:   Fri Sep 18 15:40:17 2020 -0400

    binder: Allow binding based on address spaces

commit 37dc13fc0a0d9ebc1653daab256218dfa1690203
Author: Michael Altizer <mialtize@cisco.com>
Date:   Fri Sep 18 15:40:17 2020 -0400

    binder: Allow directional binding based on interfaces

commit 9fdb963c5382952289f45a5c84a3f12389ecd988
Author: Michael Altizer <mialtize@cisco.com>
Date:   Fri Sep 18 15:40:17 2020 -0400

    binder: Enforce directionality, add intfs, rename groups, cleanup

    - The src parameters now strictly apply to the client, while the dst
      parameters apply to the server.  Previously, it would match in either
      direction as long as all directional fields matched in a given direction.
    - The zones, src_zone, and dst_zone parameters have been renamed to
      groups, src_groups, and dst_groups.
    - The ifaces parameter has been renamed to intfs.
    - Intfs and groups can now handle the full range of legal values (int32
      and int16, respectively).
    - When role is used in a session binding, it will now only apply the
      session inspector binding to the side of the conversation associated
      with the role.  (Previously, it would apply the session inspector to
      both sides.)
    - Binder configuration validation has gotten a bit stricter and more
      informative in the case of violations.

commit f6cc5b21bfbc4a0cbbedb2f57ce09f5c0623df87
Author: Michael Altizer <mialtize@cisco.com>
Date:   Fri Sep 18 15:40:17 2020 -0400

    normalizer: Move TTL configuration toggle to inspector configure()

    This prevents non-deterministic behavior influenced by the order of the
    network and normalizer module configurations being parsed from Lua.

20 files changed:
src/flow/flow.h
src/flow/flow_control.cc
src/framework/bits.h
src/framework/parameter.cc
src/framework/value.cc
src/framework/value.h
src/main/policy.cc
src/main/policy.h
src/managers/inspector_manager.cc
src/managers/module_manager.cc
src/network_inspectors/binder/CMakeLists.txt
src/network_inspectors/binder/bind_module.cc
src/network_inspectors/binder/bind_module.h
src/network_inspectors/binder/binder.cc
src/network_inspectors/binder/binding.cc [new file with mode: 0644]
src/network_inspectors/binder/binding.h
src/network_inspectors/normalize/norm_module.cc
src/network_inspectors/normalize/norm_module.h
src/network_inspectors/normalize/normalize.cc
src/service_inspectors/http2_inspect/http2_stream_splitter.cc

index 8177b0d4ab2a8757e4b9f74c5e75fa282d02cce6..f6d3d766e503b42136014464c01317112a7f656c 100644 (file)
@@ -274,12 +274,24 @@ public:
         ssn_client->add_ref();
     }
 
+    void clear_client()
+    {
+        ssn_client->rem_ref();
+        ssn_client = nullptr;
+    }
+
     void set_server(Inspector* ins)
     {
         ssn_server = ins;
         ssn_server->add_ref();
     }
 
+    void clear_server()
+    {
+        ssn_server->rem_ref();
+        ssn_server = nullptr;
+    }
+
     void set_clouseau(Inspector* ins)
     {
         clouseau = ins;
@@ -424,14 +436,22 @@ public:  // FIXIT-M privatize if possible
 
     uint32_t default_session_timeout;
 
+    int32_t client_intf;
+    int32_t server_intf;
+
+    int16_t client_group;
+    int16_t server_group;
+
     uint16_t client_port;
     uint16_t server_port;
 
     uint16_t ssn_policy;
     uint16_t session_state;
 
-    uint8_t inner_client_ttl, inner_server_ttl;
-    uint8_t outer_client_ttl, outer_server_ttl;
+    uint8_t inner_client_ttl;
+    uint8_t inner_server_ttl;
+    uint8_t outer_client_ttl;
+    uint8_t outer_server_ttl;
 
     uint8_t response_count;
 
index d2286c700d86e3aa9ee071cc438ad249cbf7e5ad..dd573692b9cba25781d246dd1fc1731a933af1b0 100644 (file)
@@ -204,8 +204,9 @@ static void init_roles_ip(const Packet* p, Flow* flow)
     flow->server_ip = *p->ptrs.ip_api.get_dst();
 }
 
-static void init_roles_tcp(const Packet* p, Flow* flow)
+static bool init_roles_tcp(const Packet* p, Flow* flow)
 {
+    bool swapped;
     if ( p->ptrs.tcph->is_syn_only() )
     {
         flow->ssn_state.direction = FROM_CLIENT;
@@ -213,6 +214,7 @@ static void init_roles_tcp(const Packet* p, Flow* flow)
         flow->client_port = p->ptrs.sp;
         flow->server_ip = *p->ptrs.ip_api.get_dst();
         flow->server_port = p->ptrs.dp;
+        swapped = false;
     }
     else if ( p->ptrs.tcph->is_syn_ack() )
     {
@@ -221,6 +223,7 @@ static void init_roles_tcp(const Packet* p, Flow* flow)
         flow->client_port = p->ptrs.dp;
         flow->server_ip = *p->ptrs.ip_api.get_src();
         flow->server_port = p->ptrs.sp;
+        swapped = true;
     }
     else if (p->ptrs.sp > p->ptrs.dp)
     {
@@ -229,6 +232,7 @@ static void init_roles_tcp(const Packet* p, Flow* flow)
         flow->client_port = p->ptrs.sp;
         flow->server_ip = *p->ptrs.ip_api.get_dst();
         flow->server_port = p->ptrs.dp;
+        swapped = false;
     }
     else
     {
@@ -237,7 +241,9 @@ static void init_roles_tcp(const Packet* p, Flow* flow)
         flow->client_port = p->ptrs.dp;
         flow->server_ip = *p->ptrs.ip_api.get_src();
         flow->server_port = p->ptrs.sp;
+        swapped = true;
     }
+    return swapped;
 }
 
 static void init_roles_udp(const Packet* p, Flow* flow)
@@ -249,8 +255,9 @@ static void init_roles_udp(const Packet* p, Flow* flow)
     flow->server_port = p->ptrs.dp;
 }
 
-static void init_roles_user(const Packet* p, Flow* flow)
+static bool init_roles_user(const Packet* p, Flow* flow)
 {
+    bool swapped;
     if ( p->ptrs.decode_flags & DECODE_C2S )
     {
         flow->ssn_state.direction = FROM_CLIENT;
@@ -258,6 +265,7 @@ static void init_roles_user(const Packet* p, Flow* flow)
         flow->client_port = p->ptrs.sp;
         flow->server_ip = *p->ptrs.ip_api.get_dst();
         flow->server_port = p->ptrs.dp;
+        swapped = false;
     }
     else
     {
@@ -266,35 +274,54 @@ static void init_roles_user(const Packet* p, Flow* flow)
         flow->client_port = p->ptrs.dp;
         flow->server_ip = *p->ptrs.ip_api.get_src();
         flow->server_port = p->ptrs.sp;
+        swapped = true;
     }
+    return swapped;
 }
 
 // FIXIT-L init_roles should take const Packet*
 static void init_roles(Packet* p, Flow* flow)
 {
+    bool swapped = false;
     switch ( flow->pkt_type )
     {
-    case PktType::IP:
-    case PktType::ICMP:
-        init_roles_ip(p, flow);
-        break;
+        case PktType::IP:
+        case PktType::ICMP:
+            init_roles_ip(p, flow);
+            break;
 
-    case PktType::TCP:
-        init_roles_tcp(p, flow);
-        break;
+        case PktType::TCP:
+            swapped = init_roles_tcp(p, flow);
+            break;
 
-    case PktType::UDP:
-        init_roles_udp(p, flow);
-        break;
+        case PktType::UDP:
+            init_roles_udp(p, flow);
+            break;
 
-    case PktType::PDU:
-    case PktType::FILE:
-        init_roles_user(p, flow);
-        break;
+        case PktType::PDU:
+        case PktType::FILE:
+            swapped = init_roles_user(p, flow);
+            break;
 
-    default:
-        break;
+        default:
+            break;
+    }
+
+    if (swapped)
+    {
+        flow->client_intf = p->pkth->egress_index;
+        flow->server_intf = p->pkth->ingress_index;
+        flow->client_group = p->pkth->egress_group;
+        flow->server_group = p->pkth->ingress_group;
     }
+    else
+    {
+        flow->client_intf = p->pkth->ingress_index;
+        flow->server_intf = p->pkth->egress_index;
+        flow->client_group = p->pkth->ingress_group;
+        flow->server_group = p->pkth->egress_group;
+    }
+
     flow->flags.app_direction_swapped = false;
     if ( flow->ssn_state.direction == FROM_CLIENT )
         p->packet_flags |= PKT_FROM_CLIENT;
index e4fa8e9203faa70270d74dff762ed27acea7b46b..60fe4c7d3ba0b01495846df38d264e72a8b304e6 100644 (file)
@@ -27,7 +27,6 @@
 typedef std::bitset<65536> PortBitSet;
 typedef std::bitset<4096> VlanBitSet;
 typedef std::bitset<256> ByteBitSet;
-typedef std::bitset<64> ZoneBitSet;
 
 #endif
 
index ec15144ffa18de4269bef8d18dd3587bec69278a..3deed16a52df7f44110e0582d30d5a318e83bb60 100644 (file)
@@ -126,13 +126,13 @@ int64_t Parameter::get_int(const char* r)
 // validation methods
 //--------------------------------------------------------------------------
 
-static bool valid_bool(Value& v, const char*)
+static bool valid_bool(const Value& v, const char*)
 {
     return v.get_type() == Value::VT_BOOL;
 }
 
 // FIXIT-L allow multiple , separated ranges
-static bool valid_int(Value& v, const char* r)
+static bool valid_int(const Value& v, const char* r)
 {
     if ( v.get_type() != Value::VT_NUM )
         return false;
@@ -183,11 +183,11 @@ static bool valid_int(Value& v, const char* r)
 // their actual, specific semantics instead of trying to explain the syntax.  this
 // also ensures that an int-type range is not applied to a string.
 
-static bool valid_interval(Value&, const char*)
+static bool valid_interval(const Value&, const char*)
 { return true; }
 
 // FIXIT-L allow multiple , separated ranges
-static bool valid_real(Value& v, const char* r)
+static bool valid_real(const Value& v, const char* r)
 {
     if ( v.get_type() != Value::VT_NUM )
         return false;
@@ -224,7 +224,7 @@ static bool valid_real(Value& v, const char* r)
     return true;
 }
 
-static bool valid_string(Value& v, const char* r)
+static bool valid_string(const Value& v, const char* r)
 {
     if ( v.get_type() != Value::VT_STR )
         return false;
@@ -241,7 +241,7 @@ static bool valid_string(Value& v, const char* r)
     return len <= max;
 }
 
-static bool valid_select(Value& v, const char* r)
+static bool valid_select(const Value& v, const char* r)
 {
     if ( v.get_type() != Value::VT_STR )
         return false;
@@ -554,7 +554,7 @@ TEST_CASE("bool", "[Parameter]")
 struct
 {
     bool expected;
-    bool (*validate)(Value&, const char*);
+    bool (*validate)(const Value&, const char*);
     double value;
     const char* range;
 }
@@ -621,11 +621,11 @@ TEST_CASE("num", "[Parameter]")
 struct
 {
     bool expected;
-    bool (*validate)(Value&, const char*);
+    bool (*validate)(const Value&, const char*);
     const char* value;
     const char* range;
 }
-string_tests[] =
+const_string_tests[] =
 {
 // __STRDUMP_DISABLE__
     { true, valid_string, "green", "(optional)" },
@@ -638,6 +638,20 @@ string_tests[] =
     { false, valid_select, "blue", "red | green | yellow" },
     { false, valid_select, "green", nullptr },
 
+    { false, nullptr, 0, nullptr }
+// __STRDUMP_ENABLE__
+};
+
+struct
+{
+    bool expected;
+    bool (*validate)(Value&, const char*);
+    const char* value;
+    const char* range;
+}
+string_tests[] =
+{
+// __STRDUMP_DISABLE__
     { true, valid_enum, "green", "red | green | yellow" },
     { false, valid_enum, "blue", "red | green | yellow" },
     { false, valid_enum, "green", nullptr },
@@ -680,8 +694,16 @@ string_tests[] =
 
 TEST_CASE("string", "[Parameter]")
 {
-    auto test = string_tests;
+    auto ctest = const_string_tests;
+    while ( ctest->validate )
+    {
+        Value v(ctest->value);
+        bool result = ctest->validate(v, ctest->range);
+        CHECK(result == ctest->expected);
+        ++ctest;
+    }
 
+    auto test = string_tests;
     while ( test->validate )
     {
         Value v(test->value);
index 9f1d9746e642364adf564b42038dcd73bc0dd297..16bdf0c450283f607f4ce0ab188cadd9c5471a90 100644 (file)
@@ -108,19 +108,6 @@ void Value::get_bits(PortBitSet& list) const
     }
 }
 
-void Value::get_bits(ZoneBitSet& list) const
-{
-    list.reset();
-    std::size_t len = str.size();
-    assert(len == list.size());
-
-    for ( std::size_t n = 0; n < len; ++n )
-    {
-        if ( str[n] == '1' )
-            list.set(n);
-    }
-}
-
 void Value::get_bits(VlanBitSet& list) const
 {
     list.reset();
index aa93044f08976cd1666cf87464fa86342137d328..2934c1fc1a9a88a19f90cba6d9b41ea440ac9a3b 100644 (file)
@@ -76,7 +76,7 @@ public:
         return *this;
     }
 
-    ValueType get_type()
+    ValueType get_type() const
     { return type; }
 
     ~Value()
@@ -176,7 +176,6 @@ public:
     void get_bits(PortBitSet&) const;
     void get_bits(VlanBitSet&) const;
     void get_bits(ByteBitSet&) const;
-    void get_bits(ZoneBitSet&) const;
 
     void lower()
     { std::transform(str.begin(), str.end(), str.begin(), ::tolower); }
index 5c12f4438f0503cde735f0ffc1472a7359212f8c..19df09fc7fb0a582c56ddf6323645f64a0973e3a 100644 (file)
@@ -151,7 +151,7 @@ PolicyMap::PolicyMap(PolicyMap* other_map)
     {
         add_shell(new Shell(nullptr, true));
         empty_ips_policy = new IpsPolicy(ips_policy.size());
-        ips_policy.emplace_back(empty_ips_policy);
+        ips_policy.push_back(empty_ips_policy);
     }
 
     set_inspection_policy(inspection_policy[0]);
@@ -232,40 +232,36 @@ void PolicyMap::clone(PolicyMap *other_map)
     user_network = other_map->user_network;
 }
 
-unsigned PolicyMap::add_inspection_shell(Shell* sh)
+InspectionPolicy* PolicyMap::add_inspection_shell(Shell* sh)
 {
     unsigned idx = inspection_policy.size();
-    shells.emplace_back(sh);
-    inspection_policy.emplace_back(new InspectionPolicy(idx));
+    InspectionPolicy* p = new InspectionPolicy(idx);
 
-    shell_map[sh] = std::make_shared<PolicyTuple>(inspection_policy.back(), nullptr, nullptr);
-    return idx;
+    shells.push_back(sh);
+    inspection_policy.push_back(p);
+    shell_map[sh] = std::make_shared<PolicyTuple>(p, nullptr, nullptr);
+
+    return p;
 }
 
-unsigned PolicyMap::add_ips_shell(Shell* sh)
+IpsPolicy* PolicyMap::add_ips_shell(Shell* sh)
 {
     unsigned idx = ips_policy.size();
-    shells.emplace_back(sh);
-    ips_policy.emplace_back(new IpsPolicy(idx));
-    shell_map[sh] = std::make_shared<PolicyTuple>(nullptr, ips_policy.back(), nullptr);
-    return idx;
-}
+    IpsPolicy* p = new IpsPolicy(idx);
 
-unsigned PolicyMap::add_network_shell(Shell* sh)
-{
-    unsigned idx = network_policy.size();
-    shells.emplace_back(sh);
-    network_policy.emplace_back(new NetworkPolicy(idx));
-    shell_map[sh] = std::make_shared<PolicyTuple>(nullptr, nullptr, network_policy.back());
-    return idx;
+    shells.push_back(sh);
+    ips_policy.push_back(p);
+    shell_map[sh] = std::make_shared<PolicyTuple>(nullptr, p, nullptr);
+
+    return p;
 }
 
 std::shared_ptr<PolicyTuple> PolicyMap::add_shell(Shell* sh)
 {
-    shells.emplace_back(sh);
-    inspection_policy.emplace_back(new InspectionPolicy(inspection_policy.size()));
-    ips_policy.emplace_back(new IpsPolicy(ips_policy.size()));
-    network_policy.emplace_back(new NetworkPolicy(network_policy.size()));
+    shells.push_back(sh);
+    inspection_policy.push_back(new InspectionPolicy(inspection_policy.size()));
+    ips_policy.push_back(new IpsPolicy(ips_policy.size()));
+    network_policy.push_back(new NetworkPolicy(network_policy.size()));
 
     return shell_map[sh] = std::make_shared<PolicyTuple>(inspection_policy.back(),
         ips_policy.back(), network_policy.back());
@@ -275,7 +271,7 @@ std::shared_ptr<PolicyTuple> PolicyMap::get_policies(Shell* sh)
 {
     const auto& pt = shell_map.find(sh);
 
-    return pt == shell_map.end() ? nullptr:pt->second;
+    return pt == shell_map.end() ? nullptr : pt->second;
 }
 
 //-------------------------------------------------------------------------
index 0fa0a5c6083839ab0188377d2df8e26cca92b9a1..89681ad719388aea743c1c4ebf955ba8c93707e4 100644 (file)
@@ -211,9 +211,8 @@ public:
     PolicyMap(PolicyMap* old_map = nullptr);
     ~PolicyMap();
 
-    unsigned add_inspection_shell(Shell*);
-    unsigned add_ips_shell(Shell*);
-    unsigned add_network_shell(Shell*);
+    InspectionPolicy* add_inspection_shell(Shell*);
+    IpsPolicy* add_ips_shell(Shell*);
     std::shared_ptr<PolicyTuple> add_shell(Shell*);
     std::shared_ptr<PolicyTuple> get_policies(Shell* sh);
     void clone(PolicyMap *old_map);
index d77bc3afab2fe2005db6d4a3c35ba4b209793510..f2c2da64751077e1552be5cbac8493c01e0171f2 100644 (file)
@@ -1105,7 +1105,6 @@ static inline void execute(
     }
 }
 
-// FIXIT-L use inspection events instead of exec
 void InspectorManager::bumble(Packet* p)
 {
     Flow* flow = p->flow;
index 5248f070ca0db088c91593765c63fb3d52ea5ae9..4e0206f13f58168c9d20b5f431db4ee111ec6ffc 100644 (file)
@@ -435,7 +435,7 @@ static bool ignored(const char* fqn)
 //
 // FIXIT-L presently no way to catch errors like EXTERNAL_NET = not HOME_NET
 // which becomes a bool var and is ignored.
-static bool set_var(const char* fqn, Value& v)
+static bool set_var(const char* fqn, const Value& v)
 {
     bool to_be_set = v.get_type() == Value::VT_STR;
 
index 2bb28f3ed0c1450be457dadee1149ba6d9312e2f..d74028fad79112c3068b30a9739f49696a35c7a9 100644 (file)
@@ -1,6 +1,7 @@
 
 set(FILE_LIST
     binder.cc
+    binding.cc
     binding.h
     bind_module.cc
     bind_module.h
index 93755f83da03a9d48371e5c9f0276be9785019fc..1cdcd784b16be2a9f564eb3ad2011518b17d8849 100644 (file)
 
 #include "bind_module.h"
 
+#include <iomanip>
+
 #include "log/messages.h"
 #include "main/shell.h"
 #include "main/snort_config.h"
+#include "managers/module_manager.h"
 #include "parser/parse_ip.h"
 #include "protocols/packet.h"
 
@@ -41,11 +44,15 @@ THREAD_LOCAL BindStats bstats;
 
 static const PegInfo bind_pegs[] =
 {
-    { CountType::SUM, "packets", "initial bindings" },
-    { CountType::SUM, "resets", "reset bindings" },
-    { CountType::SUM, "blocks", "block bindings" },
-    { CountType::SUM, "allows", "allow bindings" },
-    { CountType::SUM, "inspects", "inspect bindings" },
+    { CountType::SUM, "new_flows", "new flows evaluated" },
+    { CountType::SUM, "service_changes", "flow service changes evaluated" },
+    { CountType::SUM, "assistant_inspectors", "flow assistant inspector requests handled" },
+    { CountType::SUM, "new_standby_flows", "new HA flows evaluated" },
+    { CountType::SUM, "no_match", "binding evaluations that had no matches" },
+    { CountType::SUM, "resets", "reset actions bound" },
+    { CountType::SUM, "blocks", "block actions bound" },
+    { CountType::SUM, "allows", "allow actions bound" },
+    { CountType::SUM, "inspects", "inspect actions bound" },
     { CountType::END, nullptr, nullptr }
 };
 
@@ -58,12 +65,9 @@ static const Parameter binder_when_params[] =
     // FIXIT-L when.policy_id should be an arbitrary string auto converted
     // into index for binder matching and lookups
 
-    { "ips_policy_id", Parameter::PT_INT, "0:max32", "0",
+    { "ips_policy_id", Parameter::PT_INT, "0:max32", nullptr,
       "unique ID for selection of this config by external logic" },
 
-    { "ifaces", Parameter::PT_BIT_LIST, "255", nullptr,
-      "list of interface indices" },
-
     { "vlans", Parameter::PT_BIT_LIST, "4095", nullptr,
       "list of VLAN IDs" },
 
@@ -88,14 +92,26 @@ static const Parameter binder_when_params[] =
     { "dst_ports", Parameter::PT_BIT_LIST, "65535", nullptr,
       "list of destination ports" },
 
-    { "zones", Parameter::PT_BIT_LIST, "63", nullptr,
-      "zones" },
+    { "intfs", Parameter::PT_STRING, nullptr, nullptr,
+      "list of interface IDs" },
+
+    { "src_intfs", Parameter::PT_STRING, nullptr, nullptr,
+      "list of source interface IDs" },
+
+    { "dst_intfs", Parameter::PT_STRING, nullptr, nullptr,
+      "list of destination interface IDs" },
 
-    { "src_zone", Parameter::PT_BIT_LIST, "63", nullptr,
-      "source zone" },
+    { "groups", Parameter::PT_STRING, nullptr, nullptr,
+      "list of interface group IDs" },
 
-    { "dst_zone", Parameter::PT_BIT_LIST, "63", nullptr,
-      "destination zone" },
+    { "src_groups", Parameter::PT_STRING, nullptr, nullptr,
+      "list of source interface group IDs" },
+
+    { "dst_groups", Parameter::PT_STRING, nullptr, nullptr,
+      "list of destination group IDs" },
+
+    { "addr_spaces", Parameter::PT_STRING, nullptr, nullptr,
+      "list of address space IDs" },
 
     { "role", Parameter::PT_ENUM, "client | server | any", "any",
       "use the given configuration on one or any end of a session" },
@@ -103,6 +119,16 @@ static const Parameter binder_when_params[] =
     { "service", Parameter::PT_STRING, nullptr, nullptr,
       "override default configuration" },
 
+    // FIXIT-D deprecated zone parameters to be removed
+    { "zones", Parameter::PT_STRING, nullptr, nullptr,
+      "deprecated alias for groups" },
+
+    { "src_zone", Parameter::PT_STRING, nullptr, nullptr,
+      "deprecated alias for src_groups" },
+
+    { "dst_zone", Parameter::PT_STRING, nullptr, nullptr,
+      "deprecated alias for dst_groups" },
+
     { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
 };
 
@@ -143,24 +169,56 @@ static const Parameter s_params[] =
     { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
 };
 
-BinderModule::BinderModule() : Module(BIND_NAME, BIND_HELP, s_params, true)
-{ work = nullptr; }
+template<typename T>
+static bool parse_int_set(const Value& v, unordered_set<T>& set)
+{
+    assert(v.get_type() == Value::VT_STR);
+
+    set.clear();
+
+    string pl = v.get_string();
+
+    stringstream ss(pl);
+    ss >> setbase(0);
+
+    uint64_t n;
+
+    while ( ss >> n )
+    {
+        if ( n > numeric_limits<T>::max() )
+            return false;
+
+        set.insert(n);
+    }
+    if ( !ss.eof() )
+        return false;
+
+    return true;
+}
+
+BinderModule::BinderModule() : Module(BIND_NAME, BIND_HELP, s_params, true) { }
 
 BinderModule::~BinderModule()
 {
-    if ( work )
-        delete work;
+    bindings.clear();
+    binding.clear();
 }
 
 ProfileStats* BinderModule::get_profile() const
 { return &bindPerfStats; }
 
-void BinderModule::add_file(const char* name, const char* type)
+bool BinderModule::add_policy_file(const char* name, const char* type)
 {
-    work->use.name = name;
-    work->use.type = type;
-    use_name_count++;
-    use_type_count++;
+    if (!policy_type.empty())
+    {
+        ParseError("Only one type of policy may be specified per binding");
+        return false;
+    }
+
+    policy_filename = name;
+    policy_type = type;
+
+    return true;
 }
 
 static void set_ip_var(sfip_var_t*& var, const char* val)
@@ -170,40 +228,55 @@ static void set_ip_var(sfip_var_t*& var, const char* val)
     var = sfip_var_from_string(val, "binder");
 }
 
-bool BinderModule::set(const char* fqn, Value& v, SnortConfig*)
+bool BinderModule::begin(const char* fqn, int idx, SnortConfig*)
 {
-    if ( !work )
-        return false;
+    if ( idx && !strcmp(fqn, BIND_NAME) )
+    {
+        binding.clear();
+        policy_filename.clear();
+        policy_type.clear();
+    }
 
-    // both
-    else if ( !strcmp(fqn, "binder.when.service") )
-        work->when.svc = v.get_string();
+    return true;
+}
 
+bool BinderModule::set(const char* fqn, Value& v, SnortConfig*)
+{
+    // both
+    if ( !strcmp(fqn, "binder.when.service") )
+    {
+        binding.when.svc = v.get_string();
+        binding.when.add_criteria(BindWhen::Criteria::BWC_SVC);
+    }
     else if ( !strcmp(fqn, "binder.use.service") )
-        work->use.svc = v.get_string();
+        binding.use.svc = v.get_string();
 
     // when
-    else if ( v.is("ifaces") )
-        v.get_bits(work->when.ifaces);
-
+    else if ( v.is("ips_policy_id") )
+    {
+        binding.when.ips_id_user = v.get_uint32();
+        binding.when.add_criteria(BindWhen::Criteria::BWC_IPS_ID);
+    }
+    else if ( v.is("vlans") )
+    {
+        v.get_bits(binding.when.vlans);
+        binding.when.add_criteria(BindWhen::Criteria::BWC_VLANS);
+    }
     else if ( v.is("nets") )
     {
-        set_ip_var(work->when.src_nets, v.get_string());
-        unsplit_nets = true;
+        set_ip_var(binding.when.src_nets, v.get_string());
+        binding.when.add_criteria(BindWhen::Criteria::BWC_NETS);
     }
     else if ( v.is("src_nets") )
     {
-        set_ip_var(work->when.src_nets, v.get_string());
-        work->when.split_nets = true;
+        set_ip_var(binding.when.src_nets, v.get_string());
+        binding.when.add_criteria(BindWhen::Criteria::BWC_SPLIT_NETS);
     }
     else if ( v.is("dst_nets") )
     {
-        set_ip_var(work->when.dst_nets, v.get_string());
-        work->when.split_nets = true;
+        set_ip_var(binding.when.dst_nets, v.get_string());
+        binding.when.add_criteria(BindWhen::Criteria::BWC_SPLIT_NETS);
     }
-    else if ( v.is("ips_policy_id") )
-        work->when.ips_id_user = v.get_uint32();
-
     else if ( v.is("proto") )
     {
         const unsigned mask[] =
@@ -211,174 +284,243 @@ bool BinderModule::set(const char* fqn, Value& v, SnortConfig*)
             PROTO_BIT__ANY_TYPE, PROTO_BIT__IP, PROTO_BIT__ICMP,
             PROTO_BIT__TCP, PROTO_BIT__UDP, PROTO_BIT__PDU, PROTO_BIT__FILE
         };
-        work->when.protos = mask[v.get_uint8()];
+        binding.when.protos = mask[v.get_uint8()];
+        binding.when.add_criteria(BindWhen::Criteria::BWC_PROTO);
     }
     else if ( v.is("ports") )
     {
-        v.get_bits(work->when.src_ports);
-        unsplit_ports = true;
+        v.get_bits(binding.when.src_ports);
+        binding.when.add_criteria(BindWhen::Criteria::BWC_PORTS);
     }
     else if ( v.is("src_ports") )
     {
-        v.get_bits(work->when.src_ports);
-        work->when.split_ports = true;
+        v.get_bits(binding.when.src_ports);
+        binding.when.add_criteria(BindWhen::Criteria::BWC_SPLIT_PORTS);
     }
     else if ( v.is("dst_ports") )
     {
-        v.get_bits(work->when.dst_ports);
-        work->when.split_ports = true;
+        v.get_bits(binding.when.dst_ports);
+        binding.when.add_criteria(BindWhen::Criteria::BWC_SPLIT_PORTS);
     }
-
-    else if ( v.is("zones") )
+    else if ( v.is("intfs") )
     {
-        v.get_bits(work->when.src_zones);
-        unsplit_zones = true;
+        if (!parse_int_set<int32_t>(v, binding.when.src_intfs))
+            return false;
+        binding.when.add_criteria(BindWhen::Criteria::BWC_INTFS);
     }
-    else if ( v.is("src_zone") )
+    else if ( v.is("src_intfs") )
     {
-        v.get_bits(work->when.src_zones);
-        work->when.split_zones = true;
+        if (!parse_int_set<int32_t>(v, binding.when.src_intfs))
+            return false;
+        binding.when.add_criteria(BindWhen::Criteria::BWC_SPLIT_INTFS);
     }
-    else if ( v.is("dst_zone") )
+    else if ( v.is("dst_intfs") )
     {
-        v.get_bits(work->when.dst_zones);
-        work->when.split_zones = true;
+        if (!parse_int_set<int32_t>(v, binding.when.dst_intfs))
+            return false;
+        binding.when.add_criteria(BindWhen::Criteria::BWC_SPLIT_INTFS);
+    }
+    else if ( v.is("groups") || v.is("zones") )
+    {
+        if (!parse_int_set<int16_t>(v, binding.when.src_groups))
+            return false;
+        binding.when.add_criteria(BindWhen::Criteria::BWC_GROUPS);
+    }
+    else if ( v.is("src_groups") || v.is("src_zone") )
+    {
+        if (!parse_int_set<int16_t>(v, binding.when.src_groups))
+            return false;
+        binding.when.add_criteria(BindWhen::Criteria::BWC_SPLIT_GROUPS);
+    }
+    else if ( v.is("dst_groups") || v.is("dst_zone") )
+    {
+        if (!parse_int_set<int16_t>(v, binding.when.dst_groups))
+            return false;
+        binding.when.add_criteria(BindWhen::Criteria::BWC_SPLIT_GROUPS);
+    }
+    else if ( v.is("addr_spaces") )
+    {
+        if (!parse_int_set<uint16_t>(v, binding.when.addr_spaces))
+            return false;
+        binding.when.add_criteria(BindWhen::Criteria::BWC_ADDR_SPACES);
     }
-
     else if ( v.is("role") )
-        work->when.role = (BindWhen::Role)v.get_uint8();
-
-    else if ( v.is("vlans") )
-        v.get_bits(work->when.vlans);
+        binding.when.role = (BindWhen::Role)v.get_uint8();
 
     // use
     else if ( v.is("action") )
-        work->use.action = (BindUse::Action)(v.get_uint8());
+        binding.use.action = (BindUse::Action)(v.get_uint8());
 
     else if ( v.is("file") )
-        add_file(v.get_string(), FILE_KEY);
-
-    else if ( v.is("inspection_policy") )
-        add_file(v.get_string(), INSPECTION_KEY);
-
-    else if ( v.is("ips_policy") )
-        add_file(v.get_string(), IPS_KEY);
-
-    else if ( v.is("name") )
     {
-        work->use.name = v.get_string();
-        use_name_count++;
+        if (!add_policy_file(v.get_string(), FILE_KEY))
+            return false;
     }
-    else if ( v.is("type") )
+    else if ( v.is("inspection_policy") )
     {
-        work->use.type = v.get_string();
-        if ( work->use.type == "gtp" )
-            work->use.type = "gtp_inspect";
-        use_type_count++;
+        if (!add_policy_file(v.get_string(), INSPECTION_KEY))
+            return false;
     }
-    else
-        return false;
-
-    return true;
-}
-
-bool BinderModule::begin(const char* fqn, int idx, SnortConfig*)
-{
-    if ( idx && !strcmp(fqn, BIND_NAME) )
+    else if ( v.is("ips_policy") )
     {
-        work = new Binding;
-        unsplit_nets = false;
-        unsplit_ports = false;
-        unsplit_zones = false;
-        use_name_count = 0;
-        use_type_count = 0;
+        if (!add_policy_file(v.get_string(), IPS_KEY))
+            return false;
     }
+    else if ( v.is("name") )
+        binding.use.name = v.get_string();
+
+    else if ( v.is("type") )
+        binding.use.type = v.get_string();
 
     return true;
 }
 
-static void file_name_type_error()
-{ ParseError("you can't set binder.use file, detection_policy, or inspection_policy with type or name"); }
-
-static void split_nets_warning()
-{ ParseWarning(WARN_CONF, "src_nets and dst_nets override nets"); }
-
-static void split_ports_warning()
-{ ParseWarning(WARN_CONF, "src_ports and dst_ports override ports"); }
-
-static void split_zones_warning()
-{ ParseWarning(WARN_CONF, "src_zones and dst_zones override zones"); }
-
 bool BinderModule::end(const char* fqn, int idx, SnortConfig* sc)
 {
-    if ( idx && !strcmp(fqn, BIND_NAME) )
+    if ( !strcmp(fqn, BIND_NAME) && idx )
     {
-        if ( !work )
+        // When validation
+        if ( binding.when.has_criteria(BindWhen::Criteria::BWC_NETS | BindWhen::Criteria::BWC_SPLIT_NETS) )
         {
-            ParseError("invalid %s[%d]", fqn, idx);
-            return true;
+            ParseError("Can't specify 'nets' in combination with either of 'src_nets' or 'dst_nets'");
+            return false;
         }
 
-        if ( unsplit_nets && work->when.split_nets )
-            split_nets_warning();
-
-        if ( unsplit_ports && work->when.split_ports )
-            split_ports_warning();
-
-        if ( unsplit_zones && work->when.split_zones )
-            split_zones_warning();
+        if ( binding.when.has_criteria(BindWhen::Criteria::BWC_PORTS | BindWhen::Criteria::BWC_SPLIT_PORTS) )
+        {
+            ParseError("Can't specify 'ports' in combination with either of 'src_ports' or 'dst_ports'");
+            return false;
+        }
 
-        if ( use_type_count > 1 || use_name_count > 1 )
-            file_name_type_error();
+        if ( binding.when.has_criteria(BindWhen::Criteria::BWC_INTFS | BindWhen::Criteria::BWC_SPLIT_INTFS) )
+        {
+            ParseError("Can't specify 'intfs' in combination with either of 'src_intfs' or 'dst_intfs'");
+            return false;
+        }
 
-        if ( work->use.type == FILE_KEY )
+        if ( binding.when.has_criteria(BindWhen::Criteria::BWC_GROUPS | BindWhen::Criteria::BWC_SPLIT_GROUPS) )
         {
-            Shell* sh = new Shell(work->use.name.c_str());
-            auto policies = sc->policy_map->add_shell(sh);
-            work->use.inspection_index = policies->inspection->policy_id + 1;
-            work->use.ips_index = policies->ips->policy_id + 1;
+            ParseError("Can't specify 'groups' in combination with either of 'src_groups' or 'dst_groups'");
+            return false;
         }
-        else if ( work->use.type == INSPECTION_KEY )
+
+        // Use validation
+        if ( !policy_filename.empty() )
         {
-            Shell* sh = new Shell(work->use.name.c_str());
-            work->use.inspection_index = sc->policy_map->add_inspection_shell(sh) + 1;
+            // Policy binding - Co-opts the binding structure, but doesn't want most of it.
+            if ( !binding.use.svc.empty() || !binding.use.type.empty() ||
+                !binding.use.name.empty() || binding.use.action != BindUse::BA_INSPECT )
+            {
+                ParseError("Policy bindings cannot specify any other use options");
+                return false;
+            }
+
+            if ( policy_type == FILE_KEY )
+            {
+                Shell* sh = new Shell(policy_filename.c_str());
+                auto policies = sc->policy_map->add_shell(sh);
+                binding.use.inspection_index = policies->inspection->policy_id;
+                binding.use.ips_index = policies->ips->policy_id;
+            }
+            else if ( policy_type == INSPECTION_KEY )
+            {
+                Shell* sh = new Shell(policy_filename.c_str());
+                InspectionPolicy* inspection_policy = sc->policy_map->add_inspection_shell(sh);
+                binding.use.inspection_index = inspection_policy->policy_id;
+            }
+            else if ( policy_type == IPS_KEY )
+            {
+                Shell* sh = new Shell(policy_filename.c_str());
+                IpsPolicy* ips_policy = sc->policy_map->add_ips_shell(sh);
+                binding.use.ips_index = ips_policy->policy_id;
+            }
+
+            // Store the policy type and filename for verbose output
+            binding.use.type = policy_type;
+            binding.use.name = policy_filename;
+
+            commit_policy_binding();
         }
-        else if ( work->use.type == IPS_KEY )
+        else
         {
-            Shell* sh = new Shell(work->use.name.c_str());
-            work->use.ips_index = sc->policy_map->add_ips_shell(sh) + 1;
+            // Normal type binding (if name is given, it will be resolved to an inspector instance
+            //  during configure)
+            if ( !binding.use.type.empty() )
+            {
+                // Ensure that we can resolve the type to an extant module and that it is bindable
+                const char *mod_name = binding.use.type.c_str();
+                const Module* m = ModuleManager::get_module(mod_name);
+                if ( !m )
+                {
+                    ParseError("Can't bind to unknown type '%s'", mod_name);
+                    return false;
+                }
+                else if ( !m->is_bindable() )
+                {
+                    ParseError("Type '%s' is not bindable", mod_name);
+                    return false;
+                }
+                else if ( m->get_usage() == Module::GLOBAL )
+                    binding.use.global_type = true;
+
+                if ( binding.use.name.empty() )
+                    binding.use.name = binding.use.type;
+            }
+            else if ( !binding.use.name.empty() )
+            {
+                ParseError("Missing binding type for name '%s'", binding.use.name.c_str());
+                return false;
+            }
+
+            commit_binding();
         }
-
-        if ( work->use.name.empty() )
-            work->use.name = work->use.type;
-
-        bindings.emplace_back(work);
-        work = nullptr;
     }
+
     return true;
 }
 
 void BinderModule::add(const char* svc, const char* type)
 {
-    Binding* b = new Binding;
-    b->when.svc = svc;
-    b->use.type = type;
-    b->use.name = type;
-    bindings.emplace_back(b);
+    binding.clear();
+    binding.when.svc = svc;
+    binding.when.add_criteria(BindWhen::Criteria::BWC_SVC);
+    binding.use.type = type;
+    binding.use.name = type;
+    commit_binding();
 }
 
 void BinderModule::add(unsigned proto, const char* type)
 {
-    Binding* b = new Binding;
-    b->when.protos = proto;
-    b->use.type = type;
-    b->use.name = type;
-    bindings.emplace_back(b);
+    binding.clear();
+    binding.when.protos = proto;
+    binding.when.add_criteria(BindWhen::Criteria::BWC_PROTO);
+    binding.use.type = type;
+    binding.use.name = type;
+    commit_binding();
+}
+
+void BinderModule::commit_binding()
+{
+    bindings.emplace_back(binding);
+    binding.when.src_nets = nullptr;
+    binding.when.dst_nets = nullptr;
+}
+
+void BinderModule::commit_policy_binding()
+{
+    policy_bindings.emplace_back(binding);
+    binding.when.src_nets = nullptr;
+    binding.when.dst_nets = nullptr;
+}
+
+vector<Binding>& BinderModule::get_bindings()
+{
+    return bindings; // move semantics
 }
 
-vector<Binding*>& BinderModule::get_data()
+vector<Binding>& BinderModule::get_policy_bindings()
 {
-    return bindings;  // move semantics
+    return policy_bindings; // move semantics
 }
 
 const PegInfo* BinderModule::get_pegs() const
index 0c2cac964f33923d997f59f1057937e78df03f7c..5e411ceb4973982424aa8d107d9439cd81e29ce1 100644 (file)
 
 struct BindStats
 {
-    PegCount packets;
+    PegCount new_flows;
+    PegCount service_changes;
+    PegCount assistant_inspectors;
+    PegCount new_standby_flows;
+    PegCount no_match;
     PegCount verdicts[BindUse::BA_MAX];
 };
 
@@ -56,21 +60,22 @@ public:
     PegCount* get_counts() const override;
     snort::ProfileStats* get_profile() const override;
 
-    std::vector<Binding*>& get_data();
+    std::vector<Binding>& get_bindings();
+    std::vector<Binding>& get_policy_bindings();
 
     Usage get_usage() const override
     { return INSPECT; }
 
 private:
-    Binding* work;
-    std::vector<Binding*> bindings;
-    bool unsplit_nets;
-    bool unsplit_ports;
-    bool unsplit_zones;
-    unsigned use_name_count;
-    unsigned use_type_count;
-
-    void add_file(const char* name, const char* type);
+    Binding binding;
+    std::vector<Binding> bindings;
+    std::vector<Binding> policy_bindings;
+    std::string policy_filename;
+    std::string policy_type;
+
+    bool add_policy_file(const char* name, const char* type);
+    void commit_binding();
+    void commit_policy_binding();
 };
 
 #endif
index 7db2a0dabad6a101b90f2a950b26ca7ca3d2d58b..255797275fed0b785cbc0b464b43833c78c2a406 100644 (file)
 #include "config.h"
 #endif
 
-#include <iomanip>
-#include <sstream>
-
 #include "detection/detection_engine.h"
 #include "flow/flow.h"
-#include "flow/flow_key.h"
-#include "framework/data_bus.h"
 #include "log/messages.h"
-#include "main/snort_config.h"
 #include "managers/inspector_manager.h"
-#include "managers/module_manager.h"
 #include "profiler/profiler.h"
-#include "protocols/packet.h"
-#include "protocols/tcp.h"
-#include "protocols/udp.h"
 #include "pub_sub/assistant_gadget_event.h"
 #include "stream/stream.h"
 #include "stream/stream_splitter.h"
 #include "target_based/host_attributes.h"
-#include "target_based/snort_protocols.h"
 
 #include "bind_module.h"
 #include "binding.h"
@@ -50,412 +39,27 @@ using namespace std;
 
 THREAD_LOCAL ProfileStats bindPerfStats;
 
-// FIXIT-P these lookups should be optimized when the dust settles
-#define INS_IP   "stream_ip"
-#define INS_ICMP "stream_icmp"
-#define INS_TCP  "stream_tcp"
-#define INS_UDP  "stream_udp"
-#define INS_USER "stream_user"
-#define INS_FILE "stream_file"
-
-//-------------------------------------------------------------------------
-// binding
-//-------------------------------------------------------------------------
-
-Binding::Binding()
-{
-    when.split_nets = false;
-    when.src_nets = nullptr;
-    when.dst_nets = nullptr;
-
-    when.split_ports = false;
-    when.src_ports.set();
-    when.dst_ports.set();
-
-    when.split_zones = false;
-    when.src_zones.set();
-    when.dst_zones.set();
-
-    when.protos = PROTO_BIT__ANY_TYPE;
-    when.vlans.set();
-    when.ifaces.reset();
-
-    when.ips_id = 0;
-    when.ips_id_user = 0;
-    when.role = BindWhen::BR_EITHER;
-
-    use.inspection_index = 0;
-    use.ips_index = 0;
-    use.action = BindUse::BA_INSPECT;
-
-    use.what = BindUse::BW_NONE;
-    use.object = nullptr;
-}
-
-Binding::~Binding()
-{
-    if (when.src_nets)
-        sfvar_free(when.src_nets);
-
-    if (when.dst_nets)
-        sfvar_free(when.dst_nets);
-}
-
-inline bool Binding::check_ips_policy(const Flow* flow) const
-{
-    if (!when.ips_id)
-        return true;
-
-    if (when.ips_id == flow->ips_policy_id)
-        return true;
-
-    return false;
-}
-
-inline bool Binding::check_addr(const Flow* flow) const
-{
-    if (when.split_nets)
-        return true;
-
-    if (!when.src_nets)
-        return true;
-
-    switch (when.role)
-    {
-        case BindWhen::BR_SERVER:
-            if (sfvar_ip_in(when.src_nets, &flow->server_ip))
-                return true;
-            break;
-
-        case BindWhen::BR_CLIENT:
-            if (sfvar_ip_in(when.src_nets, &flow->client_ip))
-                return true;
-            break;
-
-        case BindWhen::BR_EITHER:
-            if (sfvar_ip_in(when.src_nets, &flow->client_ip) or
-                   sfvar_ip_in(when.src_nets, &flow->server_ip))
-                return true;
-            break;
-
-        default:
-            break;
-    }
-    return false;
-}
-
-inline bool Binding::check_proto(const Flow* flow) const
-{
-    if (when.protos & BIT((unsigned)flow->pkt_type))
-        return true;
-
-    return false;
-}
-
-inline bool Binding::check_iface(const Packet* p) const
-{
-    if (!p or when.ifaces.none())
-        return true;
-
-    auto in = p->pkth->ingress_index;
-    auto out = p->pkth->egress_index;
-
-    if (in > 0 and when.ifaces.test(out))
-        return true;
-
-    if (out > 0 and when.ifaces.test(in))
-        return true;
-
-    return false;
-}
-
-inline bool Binding::check_vlan(const Flow* flow) const
-{
-    unsigned v = flow->key->vlan_tag;
-    return when.vlans.test(v);
-}
-
-inline bool Binding::check_port(const Flow* flow) const
-{
-    if (when.split_ports)
-        return true;
-
-    switch (when.role)
-    {
-        case BindWhen::BR_SERVER:
-            return when.src_ports.test(flow->server_port);
-        case BindWhen::BR_CLIENT:
-            return when.src_ports.test(flow->client_port);
-        case BindWhen::BR_EITHER:
-            return (when.src_ports.test(flow->client_port) or
-                when.src_ports.test(flow->server_port));
-        default:
-            break;
-    }
-    return false;
-}
-
-inline bool Binding::check_service(const Flow* flow) const
-{
-    if (!flow->service)
-        return when.svc.empty();
-
-    if (when.svc == flow->service)
-        return true;
-
-    return false;
-}
-
-inline bool Binding::check_service(const char* service) const
-{
-    if (when.svc == service)
-        return true;
-
-    return false;
-}
-
-// we want to correlate src_zone to src_nets and src_ports, and dst_zone to dst_nets and
-// dst_ports. it doesn't matter if the packet is actually moving in the opposite direction as
-// binder is only evaluated once per flow and we need to capture the correct binding from
-// either side of the conversation
-template<typename When, typename Traffic, typename Compare>
-static Binding::DirResult directional_match(const When& when_src, const When& when_dst,
-    const Traffic& traffic_src, const Traffic& traffic_dst,
-    const Binding::DirResult dr, const Compare& compare)
-{
-    bool src_in_src = false;
-    bool src_in_dst = false;
-    bool dst_in_src = false;
-    bool dst_in_dst = false;
-    bool forward_match = false;
-    bool reverse_match = false;
-
-    switch (dr)
-    {
-        case Binding::DR_ANY_MATCH:
-            src_in_src = compare(when_src, traffic_src);
-            src_in_dst = compare(when_dst, traffic_src);
-            dst_in_src = compare(when_src, traffic_dst);
-            dst_in_dst = compare(when_dst, traffic_dst);
-
-            forward_match = src_in_src and dst_in_dst;
-            reverse_match = dst_in_src and src_in_dst;
-
-            if (forward_match and reverse_match)
-                return dr;
-
-            if (forward_match)
-                return Binding::DR_FORWARD;
-
-            if (reverse_match)
-                return Binding::DR_REVERSE;
-
-            return Binding::DR_NO_MATCH;
-
-        case Binding::DR_FORWARD:
-            src_in_src = compare(when_src, traffic_src);
-            dst_in_dst = compare(when_dst, traffic_dst);
-            return src_in_src and dst_in_dst ? dr : Binding::DR_NO_MATCH;
-
-        case Binding::DR_REVERSE:
-            src_in_dst = compare(when_dst, traffic_src);
-            dst_in_src = compare(when_src, traffic_dst);
-            return src_in_dst and dst_in_src ? dr : Binding::DR_NO_MATCH;
-
-        default:
-            break;
-    }
-
-    return Binding::DR_NO_MATCH;
-}
-
-inline Binding::DirResult Binding::check_split_addr(
-    const Flow* flow, const Packet* p, const Binding::DirResult dr) const
-{
-    if (!when.split_nets)
-        return dr;
-
-    if (!when.src_nets && !when.dst_nets)
-        return dr;
-
-    const SfIp* src_ip;
-    const SfIp* dst_ip;
-
-    if (p && p->ptrs.ip_api.is_ip())
-    {
-        src_ip = p->ptrs.ip_api.get_src();
-        dst_ip = p->ptrs.ip_api.get_dst();
-    }
-    else
-    {
-        src_ip = &flow->client_ip;
-        dst_ip = &flow->server_ip;
-    }
-
-    return directional_match(when.src_nets, when.dst_nets, src_ip, dst_ip, dr,
-        [](sfip_var_t* when_val, const SfIp* traffic_val)
-        { return when_val ? sfvar_ip_in(when_val, traffic_val) : true; });
-}
-
-inline Binding::DirResult Binding::check_split_port(
-    const Flow* flow, const Packet* p, const Binding::DirResult dr) const
-{
-    if (!when.split_ports)
-        return dr;
-
-    uint16_t src_port;
-    uint16_t dst_port;
-
-    if (!p)
-    {
-        src_port = flow->client_port;
-        dst_port = flow->server_port;
-    }
-    else if (p->is_tcp() or p->is_udp())
-    {
-        src_port = p->ptrs.sp;
-        dst_port = p->ptrs.dp;
-    }
-    else
-        return dr;
-
-    return directional_match(when.src_ports, when.dst_ports, src_port, dst_port, dr,
-        [](const PortBitSet& when_val, uint16_t traffic_val)
-        { return when_val.test(traffic_val); });
-}
-
-inline bool Binding::check_zone(const Packet* p) const
-{
-    if (when.split_zones or !p)
-        return true;
-
-    if (p->pkth->egress_group == DAQ_PKTHDR_UNKNOWN or
-        p->pkth->ingress_group == DAQ_PKTHDR_UNKNOWN)
-        return true;
-
-    assert(((unsigned)p->pkth->ingress_group) < when.src_zones.size());
-    assert(((unsigned)p->pkth->egress_group) < when.dst_zones.size());
-
-    if (when.src_zones.test((unsigned)p->pkth->ingress_group) or
-        when.dst_zones.test((unsigned)p->pkth->egress_group))
-        return true;
-    return false;
-}
-
-inline Binding::DirResult Binding::check_split_zone(const Packet* p, const Binding::DirResult dr) const
-{
-    if (!when.split_zones)
-        return dr;
-
-    int src_zone;
-    int dst_zone;
-
-    if (p)
-    {
-        src_zone = p->pkth->ingress_group;
-        dst_zone = p->pkth->egress_group;
-    }
-    else
-        return dr;
-
-    return directional_match(when.src_zones, when.dst_zones, src_zone, dst_zone, dr,
-        [](const ZoneBitSet& when_val, int traffic_val)
-        { return traffic_val == DAQ_PKTHDR_UNKNOWN ? true : when_val.test(traffic_val); });
-}
-
-bool Binding::check_all(const Flow* flow, Packet* p, const char* service) const
-{
-    Binding::DirResult dir = Binding::DR_ANY_MATCH;
-
-    if (!check_ips_policy(flow))
-        return false;
-
-    if (!check_iface(p))
-        return false;
-
-    if (!check_vlan(flow))
-        return false;
-
-    // FIXIT-M need to check role and addr/ports relative to it
-    if (!check_addr(flow))
-        return false;
-
-    dir = check_split_addr(flow, p, dir);
-    if (dir == Binding::DR_NO_MATCH)
-        return false;
-
-    if (!check_proto(flow))
-        return false;
-
-    if (!check_port(flow))
-        return false;
-
-    dir = check_split_port(flow, p, dir);
-    if (dir == Binding::DR_NO_MATCH)
-        return false;
-
-    dir = check_split_zone(p, dir);
-    if (dir == Binding::DR_NO_MATCH)
-        return false;
-
-    if (service)
-    {
-        if (!check_service(service))
-            return false;
-    }
-    else if (!check_service(flow))
-        return false;
-
-    if (!check_zone(p))
-        return false;
-
-    return true;
-}
-
 //-------------------------------------------------------------------------
 // helpers
 //-------------------------------------------------------------------------
 
-static void set_session(Flow* flow, const char* key)
-{
-    Inspector* pin = InspectorManager::get_inspector(key);
-
-    if (pin)
-    {
-        // FIXIT-M need to set ssn client and server independently
-        flow->set_client(pin);
-        flow->set_server(pin);
-        flow->clouseau = nullptr;
-    }
-}
-
-static void set_session(Flow* flow)
-{
-    flow->ssn_client = nullptr;
-    flow->ssn_server = nullptr;
-    flow->clouseau = nullptr;
-}
-
-static void set_service(Flow* flow, const HostAttributesEntry host)
-{
-    Stream::set_snort_protocol_id(flow, host, FROM_SERVER);
-}
-
-static Inspector* get_gadget(Flow* flow)
+static Inspector* get_gadget(const Flow& flow)
 {
-    if (flow->ssn_state.snort_protocol_id == UNKNOWN_PROTOCOL_ID)
+    if (flow.ssn_state.snort_protocol_id == UNKNOWN_PROTOCOL_ID)
         return nullptr;
 
-    const char* s = SnortConfig::get_conf()->proto_ref->get_name(flow->ssn_state.snort_protocol_id);
+    const SnortConfig* sc = SnortConfig::get_conf();
+    const char* s = sc->proto_ref->get_name(flow.ssn_state.snort_protocol_id);
 
     return InspectorManager::get_inspector_by_service(s);
 }
 
-static Inspector* get_gadget_by_id(const char* service)
+static Inspector* get_gadget_by_service(const char* service)
 {
     const SnortConfig* sc = SnortConfig::get_conf();
     const SnortProtocolId id = sc->proto_ref->find(service);
     const char* s = sc->proto_ref->get_name(id);
+
     return InspectorManager::get_inspector_by_service(s);
 }
 
@@ -510,16 +114,37 @@ static std::string to_string(const std::bitset<N>& bitset)
     return str;
 }
 
+template <typename T>
+static std::string to_string(const std::unordered_set<T>& set)
+{
+    if (set.empty())
+        return "";
+
+    std::vector<T> elements;
+    elements.insert(elements.end(), set.begin(), set.end());
+    std::sort(elements.begin(), elements.end());
+
+    std::stringstream ss;
+    for (auto e : elements)
+        ss << e << " ";
+
+    auto str = ss.str();
+    if (!str.empty())
+        str.pop_back();
+
+    return str;
+}
+
 static std::string to_string(const BindWhen::Role& role)
 {
     switch(role)
     {
-    case BindWhen::BR_CLIENT:
-        return "client";
-    case BindWhen::BR_SERVER:
-        return "server";
-    default:
-        return "";
+        case BindWhen::BR_CLIENT:
+            return "client";
+        case BindWhen::BR_SERVER:
+            return "server";
+        default:
+            return "";
     }
 }
 
@@ -527,20 +152,20 @@ static std::string proto_to_string(unsigned proto)
 {
     switch(proto)
     {
-    case PROTO_BIT__IP:
-        return "ip";
-    case PROTO_BIT__ICMP:
-        return "icmp";
-    case PROTO_BIT__TCP:
-        return "tcp";
-    case PROTO_BIT__UDP:
-        return "udp";
-    case PROTO_BIT__PDU:
-        return "user";
-    case PROTO_BIT__FILE:
-        return "file";
-    default:
-        return "";
+        case PROTO_BIT__IP:
+            return "ip";
+        case PROTO_BIT__ICMP:
+            return "icmp";
+        case PROTO_BIT__TCP:
+            return "tcp";
+        case PROTO_BIT__UDP:
+            return "udp";
+        case PROTO_BIT__PDU:
+            return "user";
+        case PROTO_BIT__FILE:
+            return "file";
+        default:
+            return "";
     }
 }
 
@@ -548,14 +173,14 @@ static std::string to_string(const BindUse::Action& action)
 {
     switch(action)
     {
-    case BindUse::BA_RESET:
-        return "reset";
-    case BindUse::BA_BLOCK:
-        return "block";
-    case BindUse::BA_ALLOW:
-        return "allow";
-    default:
-        return "";
+        case BindUse::BA_RESET:
+            return "reset";
+        case BindUse::BA_BLOCK:
+            return "block";
+        case BindUse::BA_ALLOW:
+            return "allow";
+        default:
+            return "";
     }
 }
 
@@ -565,51 +190,93 @@ static std::string to_string(const BindWhen& bw)
 
     when += "{";
 
-    if (bw.ips_id_user)
-        when += " ips_policy_id = " + std::to_string(bw.ips_id_user) + ",";
-
-    if (!bw.svc.empty())
-        when += " service = " + bw.svc + ",";
-
     auto role = to_string(bw.role);
     if (!role.empty())
         when += " role = " + role + ",";
 
-    auto proto = proto_to_string(bw.protos);
-    if (!proto.empty())
-        when += " proto = " + proto + ",";
+    if (bw.has_criteria(BindWhen::Criteria::BWC_IPS_ID))
+        when += " ips_policy_id = " + std::to_string(bw.ips_id_user) + ",";
 
-    auto src_nets = to_string(bw.src_nets);
-    auto dst_nets = to_string(bw.dst_nets);
+    if (bw.has_criteria(BindWhen::Criteria::BWC_VLANS))
+    {
+        auto vlans = to_string<4096>(bw.vlans);
+        when += " vlans = " + vlans + ",";
+    }
 
-    if (!src_nets.empty())
-        when += (bw.split_nets ? " src_nets = " : " nets = ") + src_nets + ",";
-    if (bw.split_nets and !dst_nets.empty())
-        when += " dst_nets = " + dst_nets + ",";
+    if (bw.has_criteria(BindWhen::Criteria::BWC_SVC))
+        when += " service = " + bw.svc + ",";
 
-    auto src_ports = to_string<65536>(bw.src_ports);
-    auto dst_ports = to_string<65536>(bw.dst_ports);
+    if (bw.has_criteria(BindWhen::Criteria::BWC_SPLIT_NETS))
+    {
+        auto src_nets = to_string(bw.src_nets);
+        auto dst_nets = to_string(bw.dst_nets);
+        if (!src_nets.empty())
+            when += " src_nets = " + src_nets + ",";
+        if (!dst_nets.empty())
+            when += " dst_nets = " + dst_nets + ",";
+    }
+    else if (bw.has_criteria(BindWhen::Criteria::BWC_NETS))
+    {
+        auto nets = to_string(bw.src_nets);
+        when += " nets = " + nets + ",";
+    }
 
-    if (!src_ports.empty())
-        when += (bw.split_ports ? " src_ports = " : " ports = ") + src_ports + ",";
-    if (bw.split_ports and !dst_ports.empty())
-        when += " dst_ports = " + dst_ports + ",";
+    if (bw.has_criteria(BindWhen::Criteria::BWC_PROTO))
+    {
+        auto proto = proto_to_string(bw.protos);
+        when += " proto = " + proto + ",";
+    }
 
-    auto src_zones = to_string<64>(bw.src_zones);
-    auto dst_zones = to_string<64>(bw.dst_zones);
+    if (bw.has_criteria(BindWhen::Criteria::BWC_SPLIT_PORTS))
+    {
+        auto src_ports = to_string<65536>(bw.src_ports);
+        auto dst_ports = to_string<65536>(bw.dst_ports);
+        if (!src_ports.empty())
+            when += " src_ports = " + src_ports + ",";
+        if (!dst_ports.empty())
+            when += " dst_ports = " + dst_ports + ",";
+    }
+    else if (bw.has_criteria(BindWhen::Criteria::BWC_PORTS))
+    {
+        auto ports = to_string<65536>(bw.src_ports);
+        when += " ports = " + ports + ",";
+    }
 
-    if (!src_zones.empty())
-        when += (bw.split_zones ? " src_zones = " : " zones = ") + src_zones + ",";
-    if (bw.split_zones and !dst_zones.empty())
-        when += " dst_zones = " + dst_zones + ",";
+    if (bw.has_criteria(BindWhen::Criteria::BWC_SPLIT_GROUPS))
+    {
+        auto src_groups = to_string<int16_t>(bw.src_groups);
+        auto dst_groups = to_string<int16_t>(bw.dst_groups);
+        if (!src_groups.empty())
+            when += " src_groups = " + src_groups + ",";
+        if (!dst_groups.empty())
+            when += " dst_groups = " + dst_groups + ",";
+    }
+    else if (bw.has_criteria(BindWhen::Criteria::BWC_GROUPS))
+    {
+        auto groups = to_string<int16_t>(bw.src_groups);
+        when += " groups = " + groups + ",";
+    }
 
-    auto ifaces = to_string<256>(bw.ifaces);
-    if (!ifaces.empty())
-        when += " ifaces = " + ifaces + ",";
+    if (bw.has_criteria(BindWhen::Criteria::BWC_SPLIT_INTFS))
+    {
+        auto src_intfs = to_string<int32_t>(bw.src_intfs);
+        auto dst_intfs = to_string<int32_t>(bw.dst_intfs);
+        if (!src_intfs.empty())
+            when += " src_intfs = " + src_intfs + ",";
+        if (!dst_intfs.empty())
+            when += " dst_intfs = " + dst_intfs + ",";
+    }
+    else if (bw.has_criteria(BindWhen::Criteria::BWC_INTFS))
+    {
+        auto intfs = to_string<int32_t>(bw.src_intfs);
+        when += " intfs = " + intfs + ",";
+    }
 
-    auto vlans = to_string<4096>(bw.vlans);
-    if (!vlans.empty())
-        when += " vlans = " + vlans + ",";
+    if (bw.has_criteria(BindWhen::Criteria::BWC_ADDR_SPACES))
+    {
+        auto addr_spaces = to_string<uint16_t>(bw.addr_spaces);
+        when += " addr_spaces = " + addr_spaces + ",";
+    }
 
     if (when.length() > 1)
         when.pop_back();
@@ -652,170 +319,138 @@ static std::string to_string(const BindUse& bu)
 
 struct Stuff
 {
-    BindUse::Action action;
-
-    Inspector* client;
-    Inspector* server;
-    Inspector* wizard;
-    Inspector* gadget;
-    Inspector* data;
+    BindUse::Action action = BindUse::BA_INSPECT;
 
-    Stuff()
-    {
-        action = BindUse::BA_INSPECT;
-        client = server = nullptr;
-        wizard = gadget = nullptr;
-        data = nullptr;
-    }
+    Inspector* client = nullptr;
+    Inspector* server = nullptr;
+    Inspector* wizard = nullptr;
+    Inspector* gadget = nullptr;
+    Inspector* data = nullptr;
 
-    bool update(Binding*);
+    bool update(const Binding&);
 
-    bool apply_action(Flow*);
-    void apply_session(Flow*, const HostAttributesEntry);
-    void apply_service(Flow*, const HostAttributesEntry);
-    void apply_assistant(Flow*, const char*);
+    void apply_action(Flow&);
+    void apply_session(Flow&, const HostAttributesEntry);
+    void apply_service(Flow&, const HostAttributesEntry);
+    void apply_assistant(Flow&, const char*);
 };
 
-bool Stuff::update(Binding* pb)
+bool Stuff::update(const Binding& pb)
 {
-    if (pb->use.action != BindUse::BA_INSPECT)
+    if (pb.use.action != BindUse::BA_INSPECT)
     {
-        action = pb->use.action;
+        action = pb.use.action;
         return true;
     }
 
-    switch (pb->use.what)
+    switch (pb.use.what)
     {
-    case BindUse::BW_NONE:
-        break;
-    case BindUse::BW_PASSIVE:
-        data = (Inspector*)pb->use.object;
-        break;
-    case BindUse::BW_CLIENT:
-        client = (Inspector*)pb->use.object;
-        break;
-    case BindUse::BW_SERVER:
-        server = (Inspector*)pb->use.object;
-        break;
-    case BindUse::BW_STREAM:
-        client = server = (Inspector*)pb->use.object;
-        break;
-    case BindUse::BW_WIZARD:
-        wizard = (Inspector*)pb->use.object;
-        return true;
-    case BindUse::BW_GADGET:
-        gadget = (Inspector*)pb->use.object;
-        return true;
-    default:
-        break;
+        case BindUse::BW_NONE:
+            break;
+        case BindUse::BW_PASSIVE:
+            data = pb.use.inspector;
+            break;
+        case BindUse::BW_CLIENT:
+            client = pb.use.inspector;
+            break;
+        case BindUse::BW_SERVER:
+            server = pb.use.inspector;
+            break;
+        case BindUse::BW_STREAM:
+            client = server = pb.use.inspector;
+            break;
+        case BindUse::BW_WIZARD:
+            wizard = pb.use.inspector;
+            return true;
+        case BindUse::BW_GADGET:
+            gadget = pb.use.inspector;
+            return true;
+        default:
+            break;
     }
     return false;
 }
 
-bool Stuff::apply_action(Flow* flow)
+void Stuff::apply_action(Flow& flow)
 {
     switch (action)
     {
-    case BindUse::BA_RESET:
-        flow->set_state(Flow::FlowState::RESET);
-        return false;
-
-    case BindUse::BA_BLOCK:
-        flow->set_state(Flow::FlowState::BLOCK);
-        return false;
-
-    case BindUse::BA_ALLOW:
-        flow->set_state(Flow::FlowState::ALLOW);
-        return false;
-
-    default:
-        break;
+        case BindUse::BA_RESET:
+            flow.set_state(Flow::FlowState::RESET);
+            break;
+        case BindUse::BA_BLOCK:
+            flow.set_state(Flow::FlowState::BLOCK);
+            break;
+        case BindUse::BA_ALLOW:
+            flow.set_state(Flow::FlowState::ALLOW);
+            break;
+        case BindUse::BA_INSPECT:
+            flow.set_state(Flow::FlowState::INSPECT);
+            break;
+        default:
+            break;
     }
-    flow->set_state(Flow::FlowState::INSPECT);
-    return true;
 }
 
-void Stuff::apply_session(Flow* flow, const HostAttributesEntry host)
+void Stuff::apply_session(Flow& flow, const HostAttributesEntry host)
 {
-    if (server)
-    {
-        flow->set_server(server);
-
-        if (client)
-            flow->set_client(client);
-        else
-            flow->set_client(server);
+    if (client)
+        flow.set_client(client);
+    else if (flow.ssn_client)
+        flow.clear_client();
 
-        return;
-    }
+    if (server)
+        flow.set_server(server);
+    else if (flow.ssn_server)
+        flow.clear_server();
 
-    switch (flow->pkt_type)
+    switch (flow.pkt_type)
     {
-    case PktType::IP:
-        set_session(flow, INS_IP);
-        flow->ssn_policy = host ? host->get_frag_policy() : 0;
-        break;
-
-    case PktType::ICMP:
-        set_session(flow, INS_ICMP);
-        break;
-
-    case PktType::TCP:
-        set_session(flow, INS_TCP);
-        flow->ssn_policy = host ? host->get_stream_policy() : 0;
-        break;
-
-    case PktType::UDP:
-        set_session(flow, INS_UDP);
-        break;
-
-    case PktType::PDU:
-        set_session(flow, INS_USER);
-        break;
-
-    case PktType::FILE:
-        set_session(flow, INS_FILE);
-        break;
-
-    default:
-        set_session(flow);
+        case PktType::IP:
+            flow.ssn_policy = host ? host->get_frag_policy() : 0;
+            break;
+        case PktType::TCP:
+            flow.ssn_policy = host ? host->get_stream_policy() : 0;
+            break;
+        default:
+            break;
     }
 }
 
-void Stuff::apply_service(Flow* flow, const HostAttributesEntry host)
+void Stuff::apply_service(Flow& flow, const HostAttributesEntry host)
 {
     if (data)
-        flow->set_data(data);
+        flow.set_data(data);
 
     if (host)
-        set_service(flow, host);
+        Stream::set_snort_protocol_id(&flow, host, FROM_SERVER);
 
     if (!gadget)
         gadget = get_gadget(flow);
 
     if (gadget)
     {
-        if (gadget != flow->gadget)
+        if (gadget != flow.gadget)
         {
-            flow->set_gadget(gadget);
+            flow.set_gadget(gadget);
 
-            if (flow->ssn_state.snort_protocol_id == UNKNOWN_PROTOCOL_ID)
-                flow->ssn_state.snort_protocol_id = gadget->get_service();
+            if (flow.ssn_state.snort_protocol_id == UNKNOWN_PROTOCOL_ID)
+                flow.ssn_state.snort_protocol_id = gadget->get_service();
 
             DataBus::publish(SERVICE_INSPECTOR_CHANGE_EVENT, DetectionEngine::get_current_packet());
         }
     }
     else if (wizard)
-        flow->set_clouseau(wizard);
+        flow.set_clouseau(wizard);
 }
 
-void Stuff::apply_assistant(Flow* flow, const char* service)
+void Stuff::apply_assistant(Flow& flow, const char* service)
 {
     if (!gadget)
-        gadget = get_gadget_by_id(service);
+        gadget = get_gadget_by_service(service);
 
     if (gadget)
-        flow->set_assistant_gadget(gadget);
+        flow.set_assistant_gadget(gadget);
 }
 
 //-------------------------------------------------------------------------
@@ -825,7 +460,7 @@ void Stuff::apply_assistant(Flow* flow, const char* service)
 class Binder : public Inspector
 {
 public:
-    Binder(vector<Binding*>&);
+    Binder(vector<Binding>&, vector<Binding>&);
     ~Binder() override;
 
     void remove_inspector_binding(SnortConfig*, const char*) override;
@@ -835,23 +470,21 @@ public:
 
     void eval(Packet*) override { }
 
-    void add(Binding* b)
-    { bindings.emplace_back(b); }
-
-    void handle_flow_setup(Packet*);
-    void handle_flow_service_change(Flow*);
-    void handle_new_standby_flow(Flow*);
-    void handle_assistant_gadget(const char* service, Packet*);
+    void handle_flow_setup(Flow&, bool standby = false);
+    void handle_flow_service_change(Flow&);
+    void handle_assistant_gadget(const char* service, Flow&);
 
 private:
-    void set_binding(SnortConfig*, Binding*);
-    void get_bindings(Flow*, Stuff&, Packet* = nullptr, const char* = nullptr); // may be null when dealing with HA flows
-    void apply(Flow*, Stuff&);
-    void apply_assistant(Flow*, Stuff&, const char*);
-    Inspector* find_gadget(Flow*);
+    void get_policy_bindings(Flow&, const char* service);
+    void get_bindings(Flow&, Stuff&, const char* service = nullptr);
+    void apply(Flow&, Stuff&);
+    void apply_assistant(Flow&, Stuff&, const char*);
+    Inspector* find_gadget(Flow&);
 
 private:
-    vector<Binding*> bindings;
+    vector<Binding> bindings;
+    vector<Binding> policy_bindings;
+    Inspector* default_ssn_inspectors[to_utype(PktType::MAX)]{};
 };
 
 class FlowStateSetupHandler : public DataHandler
@@ -859,11 +492,11 @@ class FlowStateSetupHandler : public DataHandler
 public:
     FlowStateSetupHandler() : DataHandler(BIND_NAME) { }
 
-    void handle(DataEvent& event, Flow* flow) override
+    void handle(DataEvent&, Flow* flow) override
     {
         Binder* binder = InspectorManager::get_binder();
         if (binder && flow)
-            binder->handle_flow_setup(const_cast<Packet*>(event.get_packet()));
+            binder->handle_flow_setup(*flow);
     }
 };
 
@@ -877,7 +510,7 @@ public:
     {
         Binder* binder = InspectorManager::get_binder();
         if (binder && flow)
-            binder->handle_flow_service_change(flow);
+            binder->handle_flow_service_change(*flow);
     }
 };
 
@@ -890,7 +523,7 @@ public:
     {
         Binder* binder = InspectorManager::get_binder();
         if (binder && flow)
-            binder->handle_new_standby_flow(flow);
+            binder->handle_flow_setup(*flow, true);
     }
 };
 
@@ -899,48 +532,55 @@ class AssistantGadgetHandler : public DataHandler
 public:
     AssistantGadgetHandler() : DataHandler(BIND_NAME) { }
 
-    void handle(DataEvent& event, Flow*) override
+    void handle(DataEvent& event, Flow* flow) override
     {
         Binder* binder = InspectorManager::get_binder();
         AssistantGadgetEvent* assistant_event = (AssistantGadgetEvent*)&event;
 
-        if (binder)
-            binder->handle_assistant_gadget(assistant_event->get_service(),
-                assistant_event->get_packet());
+        if (binder && flow)
+            binder->handle_assistant_gadget(assistant_event->get_service(), *flow);
     }
 };
 
-Binder::Binder(vector<Binding*>& v)
+Binder::Binder(vector<Binding>& bv, vector<Binding>& pbv)
 {
-    bindings = std::move(v);
+    bindings = std::move(bv);
+    policy_bindings = std::move(pbv);
 }
 
 Binder::~Binder()
 {
-    for (auto* p : bindings)
-        delete p;
+    for (Binding& b : bindings)
+        b.clear();
+
+    for (Binding& b : policy_bindings)
+        b.clear();
 }
 
 bool Binder::configure(SnortConfig* sc)
 {
-    unsigned sz = bindings.size();
+    for (Binding& b : bindings)
+        b.configure(sc);
 
-    for (unsigned i = 0; i < sz; i++)
-    {
-        Binding* pb = bindings[i];
+    for (Binding& b : policy_bindings)
+        b.configure(sc);
 
-        // Update with actual policy indices instead of user provided names
-        if (pb->when.ips_id_user)
+    // Grab default session inspectors if they exist for this policy
+    for (int proto = to_utype(PktType::NONE); proto < to_utype(PktType::MAX); proto++)
+    {
+        const char* name;
+        switch (static_cast<PktType>(proto))
         {
-            IpsPolicy* p = sc->policy_map->get_user_ips(pb->when.ips_id_user);
-            if (p)
-                pb->when.ips_id = p->policy_id;
-            else
-                ParseError("can't bind. ips_policy_id %u does not exist", pb->when.ips_id);
+            case PktType::IP:   name = "stream_ip"; break;
+            case PktType::TCP:  name = "stream_tcp"; break;
+            case PktType::UDP:  name = "stream_udp"; break;
+            case PktType::ICMP: name = "stream_icmp"; break;
+            case PktType::PDU:  name = "stream_user"; break;
+            case PktType::FILE: name = "stream_file"; break;
+            default:            name = nullptr; break;
         }
-
-        if (!pb->use.ips_index and !pb->use.inspection_index)
-            set_binding(sc, pb);
+        if (name)
+            default_ssn_inspectors[proto] = InspectorManager::get_inspector(name);
     }
 
     DataBus::subscribe(FLOW_STATE_SETUP_EVENT, new FlowStateSetupHandler());
@@ -953,17 +593,26 @@ bool Binder::configure(SnortConfig* sc)
 
 void Binder::show(const SnortConfig*) const
 {
-    std::once_flag once;
+    std::once_flag b_once;
 
-    if (!bindings.size())
-        return;
+    for (const Binding& b : bindings)
+    {
+        std::call_once(b_once, []{ ConfigLogger::log_option("bindings"); });
 
-    for (const auto& b : bindings)
+        auto bind_when = "{ when = " + to_string(b.when) + ",";
+        auto bind_use = "use = " + to_string(b.use) + " }";
+        ConfigLogger::log_list("", bind_when.c_str(), "   ");
+        ConfigLogger::log_list("", bind_use.c_str(), "   ", true);
+    }
+
+    std::once_flag pb_once;
+
+    for (const Binding& b : policy_bindings)
     {
-        std::call_once(once, []{ ConfigLogger::log_option("bindings"); });
+        std::call_once(pb_once, []{ ConfigLogger::log_option("policy_bindings"); });
 
-        auto bind_when = "{ when = " + to_string(b->when) + ",";
-        auto bind_use = "use = " + to_string(b->use) + " }";
+        auto bind_when = "{ when = " + to_string(b.when) + ",";
+        auto bind_use = "use = " + to_string(b.use) + " }";
         ConfigLogger::log_list("", bind_when.c_str(), "   ");
         ConfigLogger::log_list("", bind_use.c_str(), "   ", true);
     }
@@ -971,265 +620,207 @@ void Binder::show(const SnortConfig*) const
 
 void Binder::remove_inspector_binding(SnortConfig*, const char* name)
 {
-    vector<Binding*>::iterator it;
-    for (it = bindings.begin(); it != bindings.end(); ++it)
+    for (auto it = bindings.begin(); it != bindings.end(); ++it)
     {
         const char* key;
-        Binding *pb = *it;
-        if (pb->use.svc.empty())
-            key = pb->use.name.c_str();
+        const Binding &b = *it;
+        if (b.use.svc.empty())
+            key = b.use.name.c_str();
         else
-            key = pb->use.svc.c_str();
+            key = b.use.svc.c_str();
         if (!strcmp(key, name))
         {
             bindings.erase(it);
-            delete pb;
             return;
         }
     }
 }
 
-void Binder::handle_flow_setup(Packet* p)
+void Binder::handle_flow_setup(Flow& flow, bool standby)
 {
     Profile profile(bindPerfStats);
-    Stuff stuff;
-    Flow* flow = p->flow;
 
-    get_bindings(flow, stuff, p);
+    Stuff stuff;
+    get_bindings(flow, stuff);
     apply(flow, stuff);
 
-    ++bstats.verdicts[stuff.action];
-    ++bstats.packets;
+    if (standby)
+        bstats.new_standby_flows++;
+    else
+        bstats.new_flows++;
+    bstats.verdicts[stuff.action]++;
 }
 
-void Binder::handle_flow_service_change(Flow* flow)
+void Binder::handle_flow_service_change(Flow& flow)
 {
     Profile profile(bindPerfStats);
 
-    assert(flow);
-
     Inspector* ins = nullptr;
-    if (flow->service)
+    if (flow.service)
     {
         ins = find_gadget(flow);
-        if (flow->gadget != ins)
+        if (flow.gadget != ins)
         {
-            if (flow->gadget)
-                flow->clear_gadget();
+            if (flow.gadget)
+                flow.clear_gadget();
             if (ins)
             {
-                flow->set_gadget(ins);
-                flow->ssn_state.snort_protocol_id = ins->get_service();
+                flow.set_gadget(ins);
+                flow.ssn_state.snort_protocol_id = ins->get_service();
                 DataBus::publish(SERVICE_INSPECTOR_CHANGE_EVENT, DetectionEngine::get_current_packet());
             }
             else
-                flow->ssn_state.snort_protocol_id = UNKNOWN_PROTOCOL_ID;
+                flow.ssn_state.snort_protocol_id = UNKNOWN_PROTOCOL_ID;
         }
     }
     else
     {
         // reset to wizard when service is not specified
-        unsigned sz = bindings.size();
-        for (unsigned i = 0; i < sz; i++)
+        for (const Binding& b : bindings)
         {
-            Binding* pb = bindings[i];
-            if (pb->use.ips_index or pb->use.inspection_index)
-                continue;
-
-            if (pb->use.what == BindUse::BW_WIZARD)
+            if (b.use.what == BindUse::BW_WIZARD)
             {
-                ins = (Inspector*)pb->use.object;
+                ins = b.use.inspector;
                 break;
             }
         }
 
-        if (flow->gadget)
-            flow->clear_gadget();
-        if (flow->clouseau)
-            flow->clear_clouseau();
+        if (flow.gadget)
+            flow.clear_gadget();
+        if (flow.clouseau)
+            flow.clear_clouseau();
         if (ins)
-        {
-            flow->set_clouseau(ins);
-        }
-        flow->ssn_state.snort_protocol_id = UNKNOWN_PROTOCOL_ID;
+            flow.set_clouseau(ins);
+        flow.ssn_state.snort_protocol_id = UNKNOWN_PROTOCOL_ID;
     }
 
     // If there is no inspector bound to this flow after the service change, see if there's at least
     // an associated protocol ID.
-    if (!ins && flow->service)
-        flow->ssn_state.snort_protocol_id = SnortConfig::get_conf()->proto_ref->find(flow->service);
-
-    if (!flow->is_stream())
-        return;
+    if (!ins && flow.service)
+        flow.ssn_state.snort_protocol_id = SnortConfig::get_conf()->proto_ref->find(flow.service);
 
-    if (ins)
-    {
-        Stream::set_splitter(flow, true, ins->get_splitter(true));
-        Stream::set_splitter(flow, false, ins->get_splitter(false));
-    }
-    else
+    if (flow.is_stream())
     {
-        Stream::set_splitter(flow, true, new AtomSplitter(true));
-        Stream::set_splitter(flow, false, new AtomSplitter(false));
+        if (ins)
+        {
+            Stream::set_splitter(&flow, true, ins->get_splitter(true));
+            Stream::set_splitter(&flow, false, ins->get_splitter(false));
+        }
+        else
+        {
+            Stream::set_splitter(&flow, true, new AtomSplitter(true));
+            Stream::set_splitter(&flow, false, new AtomSplitter(false));
+        }
     }
+
+    bstats.service_changes++;
 }
 
-void Binder::handle_new_standby_flow(Flow* flow)
+void Binder::handle_assistant_gadget(const char* service, Flow& flow)
 {
     Profile profile(bindPerfStats);
 
     Stuff stuff;
-    get_bindings(flow, stuff);
-    apply(flow, stuff);
+    get_bindings(flow, stuff, service);
+    apply_assistant(flow, stuff, service);
 
-    ++bstats.verdicts[stuff.action];
+    bstats.assistant_inspectors++;
 }
 
-void Binder::handle_assistant_gadget(const char* service, Packet* p)
+void Binder::get_policy_bindings(Flow& flow, const char* service)
 {
-    Profile profile(bindPerfStats);
-    Stuff stuff;
-    Flow* flow = p->flow;
+    const SnortConfig* sc = SnortConfig::get_conf();
+    unsigned inspection_index = 0;
+    unsigned ips_index = 0;
 
-    get_bindings(flow, stuff, p, service);
-    apply_assistant(flow, stuff, service);
-}
+    // FIXIT-L This will select the first policy ID of each type that it finds and ignore the rest.
+    //          It gets potentially hairy if people start specifying overlapping policy types in
+    //          overlapping rules.
+    for (const Binding& b : policy_bindings)
+    {
+        // Skip any rules that don't contain an ID for a policy type we haven't set yet.
+        if ((!b.use.inspection_index || inspection_index) && (!b.use.ips_index || ips_index))
+            continue;
 
-//-------------------------------------------------------------------------
-// implementation stuff
-//-------------------------------------------------------------------------
+        if (!b.check_all(flow, service))
+            continue;
 
-void Binder::set_binding(SnortConfig* sc, Binding* pb)
-{
-    if (pb->use.action != BindUse::BA_INSPECT)
-        return;
+        if (b.use.inspection_index && !inspection_index)
+            inspection_index = b.use.inspection_index;
 
-    const char *mod_name = pb->use.type.c_str();
-    Module* m = ModuleManager::get_module(mod_name);
+        if (b.use.ips_index && !ips_index)
+            ips_index = b.use.ips_index;
+    }
 
-    if ( m and !m->is_bindable() )
+    if (inspection_index)
     {
-        ParseError("can't bind %s", mod_name);
-        return;
+        set_inspection_policy(sc, inspection_index);
+        if (!service)
+            flow.inspection_policy_id = inspection_index;
     }
 
-    bool is_global = m ? m->get_usage() == Module::GLOBAL : false;
-
-    const char* key = pb->use.name.c_str();
-    pb->use.object = InspectorManager::get_inspector(key, is_global, sc);
-
-    if (pb->use.object)
+    if (ips_index)
     {
-        switch (((Inspector*)pb->use.object)->get_api()->type)
-        {
-        case IT_STREAM: pb->use.what = BindUse::BW_STREAM; break;
-        case IT_WIZARD: pb->use.what = BindUse::BW_WIZARD; break;
-        case IT_SERVICE: pb->use.what = BindUse::BW_GADGET; break;
-        case IT_PASSIVE: pb->use.what = BindUse::BW_PASSIVE; break;
-        default: break;
-        }
+        set_ips_policy(sc, ips_index);
+        if (!service)
+            flow.ips_policy_id = ips_index;
     }
-    if (!pb->use.object)
-        pb->use.what = BindUse::BW_NONE;
-
-    if (pb->use.what == BindUse::BW_NONE)
-        ParseError("can't bind %s", key);
 }
 
 // FIXIT-P this is a simple linear search until functionality is nailed
 // down.  performance should be the focus of the next iteration.
-void Binder::get_bindings(Flow* flow, Stuff& stuff, Packet* p, const char* service)
+void Binder::get_bindings(Flow& flow, Stuff& stuff, const char* service)
 {
-    unsigned sz = bindings.size();
-
     // Evaluate policy ID bindings first
-    // FIXIT-P The way these are being used, the policy bindings should be a separate list if not a
-    //          separate table entirely
-    // FIXIT-L This will select the first policy ID of each type that it finds and ignore the rest.
-    //          It gets potentially hairy if people start specifying overlapping policy types in
-    //          overlapping rules.
-    bool inspection_set = false, ips_set = false;
-    const SnortConfig* sc = SnortConfig::get_conf();
-
-    for (unsigned i = 0; i < sz; i++)
-    {
-        Binding* pb = bindings[i];
-
-        // Skip any rules that don't contain an ID for a policy type we haven't set yet.
-        if ((!pb->use.inspection_index or inspection_set) and
-             (!pb->use.ips_index or ips_set))
-            continue;
-
-        if (!pb->check_all(flow, p, service))
-            continue;
-
-        if (pb->use.inspection_index and !inspection_set)
-        {
-            set_inspection_policy(sc, pb->use.inspection_index - 1);
-            if (!service)
-                flow->inspection_policy_id = pb->use.inspection_index - 1;
-            inspection_set = true;
-        }
-
-        if (pb->use.ips_index and !ips_set)
-        {
-            set_ips_policy(sc, pb->use.ips_index - 1);
-            if (!service)
-                flow->ips_policy_id = pb->use.ips_index - 1;
-            ips_set = true;
-        }
-
-    }
-
-    Binder* sub = InspectorManager::get_binder();
+    get_policy_bindings(flow, service);
 
     // If policy selection produced a new binder to use, use that instead.
+    Binder* sub = InspectorManager::get_binder();
     if (sub && sub != this)
     {
-        sub->get_bindings(flow, stuff, p);
+        sub->get_bindings(flow, stuff, service);
         return;
     }
 
     // If we got here, that means that a sub-policy with a binder was not invoked.
     // Continue using this binder for the rest of processing.
-    for (unsigned i = 0; i < sz; i++)
-    {
-        Binding* pb = bindings[i];
 
-        if (pb->use.ips_index or pb->use.inspection_index)
-            continue;
+    // Initialize the session inspector for both client and server to the default for this policy.
+    stuff.client = stuff.server = default_ssn_inspectors[to_utype(flow.pkt_type)];
 
-        if (!pb->check_all(flow, p, service))
+    for (const Binding& b : bindings)
+    {
+        if (!b.check_all(flow, service))
             continue;
 
-        if (stuff.update(pb))
+        if (stuff.update(b))
             return;
     }
+
+    bstats.no_match++;
 }
 
-Inspector* Binder::find_gadget(Flow* flow)
+Inspector* Binder::find_gadget(Flow& flow)
 {
     Stuff stuff;
     get_bindings(flow, stuff);
     return stuff.gadget;
 }
 
-void Binder::apply(Flow* flow, Stuff& stuff)
+void Binder::apply(Flow& flow, Stuff& stuff)
 {
-    // setup action
-    if (!stuff.apply_action(flow))
+    stuff.apply_action(flow);
+    if (flow.flow_state != Flow::FlowState::INSPECT)
         return;
 
-    const HostAttributesEntry host = HostAttributesManager::find_host(flow->server_ip);
+    const HostAttributesEntry host = HostAttributesManager::find_host(flow.server_ip);
 
-    // setup session
     stuff.apply_session(flow, host);
 
-    // setup service
     stuff.apply_service(flow, host);
 }
 
-void Binder::apply_assistant(Flow* flow, Stuff& stuff, const char* service)
+void Binder::apply_assistant(Flow& flow, Stuff& stuff, const char* service)
 {
     stuff.apply_assistant(flow, service);
 }
@@ -1247,8 +838,9 @@ static void mod_dtor(Module* m)
 static Inspector* bind_ctor(Module* m)
 {
     BinderModule* mod = (BinderModule*)m;
-    vector<Binding*>& pb = mod->get_data();
-    return new Binder(pb);
+    vector<Binding>& bv = mod->get_bindings();
+    vector<Binding>& pbv = mod->get_policy_bindings();
+    return new Binder(bv, pbv);
 }
 
 static void bind_dtor(Inspector* p)
diff --git a/src/network_inspectors/binder/binding.cc b/src/network_inspectors/binder/binding.cc
new file mode 100644 (file)
index 0000000..83dc3bf
--- /dev/null
@@ -0,0 +1,417 @@
+//--------------------------------------------------------------------------
+// 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.
+//--------------------------------------------------------------------------
+// binding.cc author Michael Altizer <mialtize@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "binding.h"
+
+#include "flow/flow.h"
+#include "flow/flow_key.h"
+#include "log/messages.h"
+#include "main/snort_config.h"
+#include "managers/inspector_manager.h"
+
+using namespace snort;
+
+Binding::Binding()
+{
+    when.src_nets = nullptr;
+    when.dst_nets = nullptr;
+    clear();
+}
+
+void Binding::clear()
+{
+    when.ips_id = 0;
+    when.ips_id_user = 0;
+    when.protos = PROTO_BIT__ANY_TYPE;
+    when.role = BindWhen::BR_EITHER;
+    when.svc.clear();
+
+    if (when.src_nets)
+    {
+        sfvar_free(when.src_nets);
+        when.src_nets = nullptr;
+    }
+    if (when.dst_nets)
+    {
+        sfvar_free(when.dst_nets);
+        when.dst_nets = nullptr;
+    }
+
+    when.vlans.reset();
+
+    when.src_ports.set();
+    when.dst_ports.set();
+
+    when.src_intfs.clear();
+    when.dst_intfs.clear();
+
+    when.src_groups.clear();
+    when.dst_groups.clear();
+
+    when.addr_spaces.clear();
+
+    when.criteria_flags = 0;
+
+    use.svc.clear();
+    use.type.clear();
+    use.name.clear();
+
+    use.action = BindUse::BA_INSPECT;
+    use.inspection_index = 0;
+    use.ips_index = 0;
+    use.what = BindUse::BW_NONE;
+    use.inspector = nullptr;
+    use.global_type = false;
+}
+
+void Binding::configure(const SnortConfig* sc)
+{
+    // Update with actual policy indices instead of user-provided identifiers
+    if (when.ips_id_user)
+    {
+        IpsPolicy* p = sc->policy_map->get_user_ips(when.ips_id_user);
+        if (p)
+            when.ips_id = p->policy_id;
+        else
+            ParseError("Can't bind for unrecognized ips_policy_id %u", when.ips_id_user);
+    }
+
+    if (use.ips_index || use.inspection_index)
+        return;
+
+    if (use.action != BindUse::BA_INSPECT)
+        return;
+
+    if (!use.name.empty())
+    {
+        const char* name = use.name.c_str();
+        Inspector* ins = InspectorManager::get_inspector(name, use.global_type, sc);
+        if (ins)
+        {
+            switch (ins->get_api()->type)
+            {
+                case IT_STREAM:
+                    switch (when.role)
+                    {
+                        case BindWhen::BR_CLIENT: use.what = BindUse::BW_CLIENT; break;
+                        case BindWhen::BR_SERVER: use.what = BindUse::BW_SERVER; break;
+                        default: use.what = BindUse::BW_STREAM; break;
+                    }
+                    break;
+                case IT_WIZARD: use.what = BindUse::BW_WIZARD; break;
+                case IT_SERVICE: use.what = BindUse::BW_GADGET; break;
+                case IT_PASSIVE: use.what = BindUse::BW_PASSIVE; break;
+                default: break;
+            }
+        }
+        if (use.what == BindUse::BW_NONE)
+            ParseError("Couldn't bind '%s'", name);
+        else
+            use.inspector = ins;
+    }
+}
+
+inline bool Binding::check_ips_policy(const Flow& flow) const
+{
+    if (!when.has_criteria(BindWhen::Criteria::BWC_IPS_ID))
+        return true;
+
+    return when.ips_id == flow.ips_policy_id;
+}
+
+inline bool Binding::check_vlan(const Flow& flow) const
+{
+    if (!when.has_criteria(BindWhen::Criteria::BWC_VLANS))
+        return true;
+
+    return when.vlans.test(flow.key->vlan_tag);
+}
+
+inline bool Binding::check_addr(const Flow& flow) const
+{
+    if (!when.has_criteria(BindWhen::Criteria::BWC_NETS))
+        return true;
+
+    switch (when.role)
+    {
+        case BindWhen::BR_SERVER:
+            if (sfvar_ip_in(when.src_nets, &flow.server_ip))
+                return true;
+            break;
+
+        case BindWhen::BR_CLIENT:
+            if (sfvar_ip_in(when.src_nets, &flow.client_ip))
+                return true;
+            break;
+
+        case BindWhen::BR_EITHER:
+            if (sfvar_ip_in(when.src_nets, &flow.client_ip) ||
+                sfvar_ip_in(when.src_nets, &flow.server_ip))
+                return true;
+            break;
+
+        default:
+            break;
+    }
+    return false;
+}
+
+inline bool Binding::check_split_addr(const Flow& flow) const
+{
+    if (!when.has_criteria(BindWhen::Criteria::BWC_SPLIT_NETS))
+        return true;
+
+    if (when.src_nets && !sfvar_ip_in(when.src_nets, &flow.client_ip))
+        return false;
+
+    if (when.dst_nets && !sfvar_ip_in(when.dst_nets, &flow.server_ip))
+        return false;
+
+    return true;
+}
+
+inline bool Binding::check_proto(const Flow& flow) const
+{
+    if (!when.has_criteria(BindWhen::Criteria::BWC_PROTO))
+        return true;
+
+    unsigned proto_bit = 1 << ((unsigned)flow.pkt_type - 1);
+    return (when.protos & proto_bit) != 0;
+}
+
+inline bool Binding::check_port(const Flow& flow) const
+{
+    if (!when.has_criteria(BindWhen::Criteria::BWC_PORTS))
+        return true;
+
+    if (flow.pkt_type != PktType::TCP && flow.pkt_type != PktType::UDP)
+        return false;
+
+    switch (when.role)
+    {
+        case BindWhen::BR_SERVER:
+            if (when.src_ports.test(flow.server_port))
+                return true;
+            break;
+
+        case BindWhen::BR_CLIENT:
+            if (when.src_ports.test(flow.client_port))
+                return true;
+            break;
+
+        case BindWhen::BR_EITHER:
+            if (when.src_ports.test(flow.client_port) ||
+                when.src_ports.test(flow.server_port))
+                return true;
+            break;
+
+        default:
+            break;
+    }
+    return false;
+}
+
+inline bool Binding::check_split_port(const Flow& flow) const
+{
+    if (!when.has_criteria(BindWhen::Criteria::BWC_SPLIT_PORTS))
+        return true;
+
+    if (flow.pkt_type != PktType::TCP && flow.pkt_type != PktType::UDP)
+        return false;
+
+    if (!when.src_ports.test(flow.client_port))
+        return false;
+
+    if (!when.dst_ports.test(flow.server_port))
+        return false;
+
+    return true;
+}
+
+inline bool Binding::check_intf(const Flow& flow) const
+{
+    if (!when.has_criteria(BindWhen::Criteria::BWC_INTFS))
+        return true;
+
+    switch (when.role)
+    {
+        case BindWhen::BR_SERVER:
+            if (when.src_intfs.count(flow.server_intf))
+                return true;
+            break;
+
+        case BindWhen::BR_CLIENT:
+            if (when.src_intfs.count(flow.client_intf))
+                return true;
+            break;
+
+        case BindWhen::BR_EITHER:
+            if (when.src_intfs.count(flow.client_intf) ||
+                when.src_intfs.count(flow.server_intf))
+                return true;
+            break;
+
+        default:
+            break;
+    }
+
+    return false;
+}
+
+inline bool Binding::check_split_intf(const Flow& flow) const
+{
+    if (!when.has_criteria(BindWhen::Criteria::BWC_SPLIT_INTFS))
+        return true;
+
+    if (!when.src_intfs.empty() && !when.src_intfs.count(flow.client_intf))
+        return false;
+
+    if (!when.dst_intfs.empty() && !when.dst_intfs.count(flow.server_intf))
+        return false;
+
+    return true;
+}
+
+inline bool Binding::check_group(const Flow& flow) const
+{
+    if (!when.has_criteria(BindWhen::Criteria::BWC_GROUPS))
+        return true;
+
+    switch (when.role)
+    {
+        case BindWhen::BR_SERVER:
+            if (when.src_groups.count(flow.server_group))
+                return true;
+            break;
+
+        case BindWhen::BR_CLIENT:
+            if (when.src_groups.count(flow.client_group))
+                return true;
+            break;
+
+        case BindWhen::BR_EITHER:
+            if (when.src_groups.count(flow.client_group) ||
+                when.src_groups.count(flow.server_group))
+                return true;
+            break;
+
+        default:
+            break;
+    }
+
+    return false;
+}
+
+inline bool Binding::check_split_group(const Flow& flow) const
+{
+    if (!when.has_criteria(BindWhen::Criteria::BWC_SPLIT_GROUPS))
+        return true;
+
+    if (!when.src_groups.empty() && !when.src_groups.count(flow.client_group))
+        return false;
+
+    if (!when.dst_groups.empty() && !when.dst_groups.count(flow.server_group))
+        return false;
+
+    return true;
+}
+
+inline bool Binding::check_address_space(const Flow& flow) const
+{
+    if (!when.has_criteria(BindWhen::Criteria::BWC_ADDR_SPACES))
+        return true;
+
+    return when.addr_spaces.count(flow.key->addressSpaceId) != 0;
+}
+
+inline bool Binding::check_service(const Flow& flow) const
+{
+    if (!when.has_criteria(BindWhen::Criteria::BWC_SVC))
+        return true;
+
+    if (!flow.service)
+        return false;
+
+    return when.svc == flow.service;
+}
+
+inline bool Binding::check_service(const char* service) const
+{
+    // Special case for explicit service lookups: Service criteria must be
+    //  specified (and match) to succeed.
+    if (!when.has_criteria(BindWhen::Criteria::BWC_SVC))
+        return false;
+
+    return when.svc == service;
+}
+
+bool Binding::check_all(const Flow& flow, const char* service) const
+{
+    // Do the service check first to optimize service change re-evaluations
+    if (service)
+    {
+        if (!check_service(service))
+            return false;
+    }
+    else if (!check_service(flow))
+        return false;
+
+    if (!check_ips_policy(flow))
+        return false;
+
+    if (!check_vlan(flow))
+        return false;
+
+    if (!check_addr(flow))
+        return false;
+
+    if (!check_split_addr(flow))
+        return false;
+
+    if (!check_proto(flow))
+        return false;
+
+    if (!check_port(flow))
+        return false;
+
+    if (!check_split_port(flow))
+        return false;
+
+    if (!check_intf(flow))
+        return false;
+
+    if (!check_split_intf(flow))
+        return false;
+
+    if (!check_group(flow))
+        return false;
+
+    if (!check_split_group(flow))
+        return false;
+
+    if (!check_address_space(flow))
+        return false;
+
+    return true;
+}
+
index 77ba0ddcecd4c9044ef660ff78236d41f2deea4f..88cf28bc0cf5f0bee88d722a9043609cfb0e486d 100644 (file)
@@ -15,7 +15,7 @@
 // with this program; if not, write to the Free Software Foundation, Inc.,
 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 //--------------------------------------------------------------------------
-// binder.cc author Russ Combs <rucombs@cisco.com>
+// binding.h author Russ Combs <rucombs@cisco.com>
 
 #ifndef BINDING_H
 #define BINDING_H
@@ -29,7 +29,8 @@
 namespace snort
 {
 class Flow;
-struct Packet;
+class Inspector;
+struct SnortConfig;
 }
 
 struct BindWhen
@@ -43,20 +44,44 @@ struct BindWhen
     Role role;
     std::string svc;
 
-    bool split_nets;
     sfip_var_t* src_nets;
     sfip_var_t* dst_nets;
 
-    ByteBitSet ifaces;
     VlanBitSet vlans;
 
-    bool split_ports;
     PortBitSet src_ports;
     PortBitSet dst_ports;
 
-    bool split_zones;
-    ZoneBitSet src_zones;
-    ZoneBitSet dst_zones;
+    std::unordered_set<int32_t> src_intfs;
+    std::unordered_set<int32_t> dst_intfs;
+
+    std::unordered_set<int16_t> src_groups;
+    std::unordered_set<int16_t> dst_groups;
+
+    std::unordered_set<uint16_t> addr_spaces;
+
+    enum Criteria
+    {
+        BWC_IPS_ID =        0x0001,
+        BWC_PROTO =         0x0002,
+        BWC_SVC =           0x0004,
+        BWC_NETS =          0x0008,
+        BWC_SPLIT_NETS =    0x0010,
+        BWC_VLANS =         0x0020,
+        BWC_PORTS =         0x0040,
+        BWC_SPLIT_PORTS =   0x0080,
+        BWC_INTFS =         0x0100,
+        BWC_SPLIT_INTFS =   0x0200,
+        BWC_GROUPS =        0x0400,
+        BWC_SPLIT_GROUPS =  0x0800,
+        BWC_ADDR_SPACES =   0x1000
+    };
+    uint16_t criteria_flags;
+
+    void add_criteria(uint16_t flags)
+    { criteria_flags |= flags; }
+    bool has_criteria(uint16_t flags) const
+    { return (criteria_flags & flags) == flags; }
 };
 
 struct BindUse
@@ -75,46 +100,34 @@ struct BindUse
     unsigned inspection_index;
     unsigned ips_index;
     What what;
-    void* object;
+    snort::Inspector* inspector;
+    bool global_type;
 };
 
 struct Binding
 {
-    enum DirResult
-    {
-        // Did not match
-        DR_NO_MATCH,
-
-        // Matched but direction could not be determined
-        DR_ANY_MATCH,
-
-        // On flow: src_* matched client, dst_* matched server
-        // On packet: src_* matched p->src_*, dst_* matched p->dst_*
-        DR_FORWARD,
-
-        // On flow: src_* matched server, dst_* matched client
-        // On packet: src_* matched p->dst_*, dst_* matched p->src_*
-        DR_REVERSE,
-    };
-
     BindWhen when;
     BindUse use;
 
     Binding();
-    ~Binding();
-
-    bool check_all(const snort::Flow*, snort::Packet*, const char* = nullptr) const;
-    bool check_ips_policy(const snort::Flow*) const;
-    bool check_iface(const snort::Packet*) const;
-    bool check_vlan(const snort::Flow*) const;
-    bool check_addr(const snort::Flow*) const;
-    DirResult check_split_addr(const snort::Flow*, const snort::Packet*, const DirResult) const;
-    bool check_proto(const snort::Flow*) const;
-    bool check_port(const snort::Flow*) const;
-    DirResult check_split_port(const snort::Flow*, const snort::Packet*, const DirResult) const;
-    bool check_zone(const snort::Packet*) const;
-    DirResult check_split_zone(const snort::Packet*, const DirResult) const;
-    bool check_service(const snort::Flow*) const;
+
+    void clear();
+    void configure(const snort::SnortConfig* sc);
+
+    bool check_all(const snort::Flow&, const char* = nullptr) const;
+    bool check_ips_policy(const snort::Flow&) const;
+    bool check_vlan(const snort::Flow&) const;
+    bool check_addr(const snort::Flow&) const;
+    bool check_split_addr(const snort::Flow&) const;
+    bool check_proto(const snort::Flow&) const;
+    bool check_port(const snort::Flow&) const;
+    bool check_split_port(const snort::Flow&) const;
+    bool check_intf(const snort::Flow&) const;
+    bool check_split_intf(const snort::Flow&) const;
+    bool check_group(const snort::Flow&) const;
+    bool check_split_group(const snort::Flow&) const;
+    bool check_address_space(const snort::Flow& flow) const;
+    bool check_service(const snort::Flow&) const;
     bool check_service(const char* service) const;
 };
 
index 40d622585acd003af765b221c637ef8c2b79c0ec..c16124923ece0a87dbd3c1b406257fac290d8612 100644 (file)
@@ -354,32 +354,6 @@ bool NormalizeModule::begin(const char* fqn, int, SnortConfig*)
     return true;
 }
 
-bool NormalizeModule::end(const char* fqn, int, SnortConfig* sc)
-{
-    if ( !strcmp(fqn, NORM_NAME) )
-    {
-        NetworkPolicy* policy = get_network_policy();
-
-        // FIXIT-M untangle these policies. this is a workaround for loading inspection-only confs
-        if ( policy == nullptr )
-        {
-            set_network_policy(sc);
-            policy = get_network_policy();
-            set_network_policy((NetworkPolicy*)nullptr);
-        }
-
-        if ( (policy->new_ttl > 1) && (policy->new_ttl >= policy->min_ttl) )
-        {
-            if ( Norm_IsEnabled(&config, NORM_IP4_BASE) )
-                Norm_Enable(&config, NORM_IP4_TTL);
-
-            if ( Norm_IsEnabled(&config, NORM_IP6_BASE) )
-                Norm_Enable(&config, NORM_IP6_TTL);
-        }
-    }
-    return true;
-}
-
 void NormalizeModule::add_test_peg(const PegInfo& norm) const
 {
     PegInfo test;
index 8e185684f452a2e4664742b5365b04c2900a3276..66460d39d3f632a4c931599829859d73a2098e53 100644 (file)
@@ -37,7 +37,6 @@ public:
 
     bool set(const char*, snort::Value&, snort::SnortConfig*) override;
     bool begin(const char*, int, snort::SnortConfig*) override;
-    bool end(const char*, int, snort::SnortConfig*) override;
 
     snort::ProfileStats* get_profile() const override;
     const PegInfo* get_pegs() const override;
index 4ded34b7197043b6ff78e8ec39b6d22e6ea45036..d5ef75a96a3e030d7b0fa62c6c6be05ca6d05c64 100644 (file)
@@ -231,11 +231,20 @@ bool Normalizer::configure(SnortConfig*)
     // network policy with whichever instantiation of an inspection policy this normalize is in
     NetworkPolicy* nap = get_network_policy();
 
-    nap->normal_mask = config.normalizer_flags;
-
     if ( nap->new_ttl && nap->new_ttl < nap->min_ttl )
         nap->new_ttl = nap->min_ttl;
 
+    if ( (nap->new_ttl > 1) && (nap->new_ttl >= nap->min_ttl) )
+    {
+        if ( Norm_IsEnabled(&config, NORM_IP4_BASE) )
+            Norm_Enable(&config, NORM_IP4_TTL);
+
+        if ( Norm_IsEnabled(&config, NORM_IP6_BASE) )
+            Norm_Enable(&config, NORM_IP6_TTL);
+    }
+
+    nap->normal_mask = config.normalizer_flags;
+
     Norm_SetConfig(&config);
     return true;
 }
index 72ed9233764aa8073095a68517b5e1e0d672fabe..a74f6d897d9abb454ef659a763a03649695b0e7f 100644 (file)
@@ -52,7 +52,7 @@ StreamSplitter::Status Http2StreamSplitter::scan(Packet* pkt, const uint8_t* dat
     if (session_data == nullptr)
     {
         AssistantGadgetEvent event(pkt, "http");
-        DataBus::publish(FLOW_ASSISTANT_GADGET_EVENT, event);
+        DataBus::publish(FLOW_ASSISTANT_GADGET_EVENT, event, pkt->flow);
         if (pkt->flow->assistant_gadget == nullptr)
             return HttpStreamSplitter::status_value(StreamSplitter::ABORT, true);
         pkt->flow->set_flow_data(session_data = new Http2FlowData(pkt->flow));