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;
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;
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;
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() )
{
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)
{
flow->client_port = p->ptrs.sp;
flow->server_ip = *p->ptrs.ip_api.get_dst();
flow->server_port = p->ptrs.dp;
+ swapped = false;
}
else
{
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)
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;
flow->client_port = p->ptrs.sp;
flow->server_ip = *p->ptrs.ip_api.get_dst();
flow->server_port = p->ptrs.dp;
+ swapped = false;
}
else
{
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;
typedef std::bitset<65536> PortBitSet;
typedef std::bitset<4096> VlanBitSet;
typedef std::bitset<256> ByteBitSet;
-typedef std::bitset<64> ZoneBitSet;
#endif
// 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;
// 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;
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;
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;
struct
{
bool expected;
- bool (*validate)(Value&, const char*);
+ bool (*validate)(const Value&, const char*);
double value;
const char* range;
}
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)" },
{ 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 },
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);
}
}
-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();
return *this;
}
- ValueType get_type()
+ ValueType get_type() const
{ return type; }
~Value()
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); }
{
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]);
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());
{
const auto& pt = shell_map.find(sh);
- return pt == shell_map.end() ? nullptr:pt->second;
+ return pt == shell_map.end() ? nullptr : pt->second;
}
//-------------------------------------------------------------------------
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);
}
}
-// FIXIT-L use inspection events instead of exec
void InspectorManager::bumble(Packet* p)
{
Flow* flow = p->flow;
//
// 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;
set(FILE_LIST
binder.cc
+ binding.cc
binding.h
bind_module.cc
bind_module.h
#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"
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 }
};
// 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" },
{ "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" },
{ "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 }
};
{ 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)
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[] =
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
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];
};
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
#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"
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);
}
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 "";
}
}
{
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 "";
}
}
{
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 "";
}
}
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();
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);
}
//-------------------------------------------------------------------------
class Binder : public Inspector
{
public:
- Binder(vector<Binding*>&);
+ Binder(vector<Binding>&, vector<Binding>&);
~Binder() override;
void remove_inspector_binding(SnortConfig*, const char*) override;
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
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);
}
};
{
Binder* binder = InspectorManager::get_binder();
if (binder && flow)
- binder->handle_flow_service_change(flow);
+ binder->handle_flow_service_change(*flow);
}
};
{
Binder* binder = InspectorManager::get_binder();
if (binder && flow)
- binder->handle_new_standby_flow(flow);
+ binder->handle_flow_setup(*flow, true);
}
};
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());
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);
}
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);
}
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)
--- /dev/null
+//--------------------------------------------------------------------------
+// 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;
+}
+
// 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
namespace snort
{
class Flow;
-struct Packet;
+class Inspector;
+struct SnortConfig;
}
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
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;
};
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;
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;
// 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;
}
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));