of the underlying decompression engine context. */
#ifndef SYNC_IN
#define SYNC_IN(dest) \
- dest->next_in = const_cast<z_const Bytef*>(SessionPtr->Next_In); \
+ dest->next_in = (Bytef*)(SessionPtr->Next_In); \
(dest)->avail_in = SessionPtr->Avail_In; \
(dest)->total_in = SessionPtr->Total_In; \
(dest)->next_out = SessionPtr->Next_Out; \
unsigned ips_policy_id;
unsigned network_policy_id;
- int32_t iface_in;
- int32_t iface_out;
-
uint16_t client_port;
uint16_t server_port;
if ( ret != Z_OK )
return nullptr;
- stream.next_in = (z_const Bytef*)s;
+ stream.next_in = (Bytef*)s;
stream.avail_in = text.size();
stream.next_out = so_buf;
if ( inflateInit2(&stream, window_bits) != Z_OK )
return nullptr;
- stream.next_in = const_cast<z_const Bytef*>(data);
+ stream.next_in = (Bytef*)data;
stream.avail_in = (uInt)len;
stream.next_out = (Bytef*)so_buf;
// binder module
//-------------------------------------------------------------------------
+#define INT32_MAX_STR "2147483647"
static const Parameter binder_when_params[] =
{
// FIXIT-L when.policy_id should be an arbitrary string auto converted
{ "dst_ports", Parameter::PT_BIT_LIST, "65535", nullptr,
"list of destination ports" },
+ { "src_zone", Parameter::PT_INT, "0:" INT32_MAX_STR, nullptr,
+ "source zone" },
+
+ { "dst_zone", Parameter::PT_INT, "0:" INT32_MAX_STR, nullptr,
+ "destination zone" },
+
{ "role", Parameter::PT_ENUM, "client | server | any", "any",
"use the given configuration on one or any end of a session" },
work->when.split_ports = true;
}
+ else if ( v.is("src_zone") )
+ work->when.src_zone = v.get_long();
+
+ else if ( v.is("dst_zone") )
+ work->when.dst_zone = v.get_long();
+
else if ( v.is("role") )
work->when.role = (BindWhen::Role)v.get_long();
#include "managers/inspector_manager.h"
#include "profiler/profiler.h"
#include "protocols/packet.h"
+#include "protocols/tcp.h"
+#include "protocols/udp.h"
#include "stream/stream.h"
#include "stream/stream_splitter.h"
#include "target_based/sftarget_reader.h"
when.protos = (unsigned)PktType::ANY;
when.vlans.set();
- when.ifaces.set();
+ when.ifaces.reset();
+
+ when.src_zone = DAQ_PKTHDR_UNKNOWN;
+ when.dst_zone = DAQ_PKTHDR_UNKNOWN;
when.ips_id = 0;
when.role = BindWhen::BR_EITHER;
return false;
}
-Binding::DirResult Binding::check_split_addr(const Flow* flow) const
-{
- if ( !when.split_nets )
- return Binding::DR_ANY_MATCH;
-
- if ( !when.src_nets && !when.dst_nets )
- return Binding::DR_ANY_MATCH;
-
- bool client_in_src = !when.src_nets or sfvar_ip_in(when.src_nets, &flow->client_ip);
- bool client_in_dst = !when.dst_nets or sfvar_ip_in(when.dst_nets, &flow->client_ip);
- bool server_in_src = !when.src_nets or sfvar_ip_in(when.src_nets, &flow->server_ip);
- bool server_in_dst = !when.dst_nets or sfvar_ip_in(when.dst_nets, &flow->server_ip);
-
- if ( client_in_src and server_in_dst )
- return Binding::DR_CLIENT_SRC;
-
- if ( server_in_src and client_in_dst )
- return Binding::DR_SERVER_SRC;
-
- return Binding::DR_NO_MATCH;
-}
-
bool Binding::check_proto(const Flow* flow) const
{
if ( when.protos & (unsigned)flow->pkt_type )
return false;
}
-bool Binding::check_iface(const Flow* flow) const
+bool Binding::check_iface(const Packet* p) const
{
- int i = flow->iface_in < 0 ? 0 : flow->iface_in;
-
- if ( when.ifaces.test(i) )
+ if ( !p or when.ifaces.none() )
return true;
- i = flow->iface_out < 0 ? 0 : flow->iface_out;
+ auto in = p->pkth->ingress_index;
+ auto out = p->pkth->egress_index;
+
+ if ( in > 0 and when.ifaces.test(out) )
+ return true;
- if ( when.ifaces.test(i) )
+ if ( out > 0 and when.ifaces.test(out) )
return true;
return false;
return false;
}
-bool Binding::check_split_port(const Flow* flow, const DirResult dr) const
+bool Binding::check_service(const Flow* flow) const
{
- if ( !when.split_ports )
+ if ( !flow->service )
+ return when.svc.empty();
+
+ if ( when.svc == flow->service )
return true;
- bool client_in_src = false;
- bool client_in_dst = false;
- bool server_in_src = false;
- bool server_in_dst = false;
+ 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>
+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:
- client_in_src = when.src_ports.test(flow->client_port);
- client_in_dst = when.dst_ports.test(flow->client_port);
- server_in_src = when.src_ports.test(flow->server_port);
- server_in_dst = when.dst_ports.test(flow->server_port);
- return ( client_in_src and server_in_dst ) or ( server_in_src and client_in_dst );
-
- case Binding::DR_CLIENT_SRC:
- client_in_src = when.src_ports.test(flow->client_port);
- server_in_dst = when.dst_ports.test(flow->server_port);
- return client_in_src and server_in_dst;
-
- case Binding::DR_SERVER_SRC:
- server_in_src = when.src_ports.test(flow->server_port);
- client_in_dst = when.dst_ports.test(flow->client_port);
- return server_in_src and client_in_dst;
+ 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 false;
+
+ return Binding::DR_NO_MATCH;
}
-bool Binding::check_service(const Flow* flow) const
+Binding::DirResult Binding::check_split_addr(const Flow* flow, const Packet* p,
+ const Binding::DirResult dr) const
{
- if ( !flow->service )
- return when.svc.empty();
+ if ( !when.split_nets )
+ return dr;
- if ( when.svc == flow->service )
- return true;
+ if ( !when.src_nets && !when.dst_nets )
+ return dr;
+
+ const SfIp* src_ip = &flow->client_ip;
+ const SfIp* dst_ip = &flow->server_ip;
- return false;
+ if ( p && p->ptrs.ip_api.is_ip() )
+ {
+ src_ip = p->ptrs.ip_api.get_src();
+ dst_ip = p->ptrs.ip_api.get_dst();
+ }
+
+ 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; });
+
+}
+
+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 = flow->client_port;
+ uint16_t dst_port = flow->server_port;
+
+ if ( p )
+ {
+ if ( p->is_tcp() )
+ {
+ src_port = p->ptrs.tcph->src_port();
+ dst_port = p->ptrs.tcph->dst_port();
+ }
+ else if ( p->is_udp() )
+ {
+ src_port = p->ptrs.udph->src_port();
+ dst_port = p->ptrs.udph->dst_port();
+ }
+ 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); });
+}
+
+Binding::DirResult Binding::check_zone(const Packet* p, const Binding::DirResult dr) const
+{
+ if ( !p )
+ return dr;
+
+ return directional_match(when.src_zone, when.dst_zone,
+ p->pkth->ingress_group, p->pkth->egress_group, dr,
+ [](int32_t when_val, int32_t zone)
+ { return when_val == DAQ_PKTHDR_UNKNOWN or when_val == zone; });
}
-bool Binding::check_all(const Flow* flow) const
+bool Binding::check_all(const Flow* flow, Packet* p) const
{
+ Binding::DirResult dir = Binding::DR_ANY_MATCH;
+
if ( !check_ips_policy(flow) )
return false;
- if ( !check_iface(flow) )
+ if ( !check_iface(p) )
return false;
if ( !check_vlan(flow) )
if ( !check_addr(flow) )
return false;
- auto dir = check_split_addr(flow);
+ dir = check_split_addr(flow, p, dir);
if ( dir == Binding::DR_NO_MATCH )
return false;
if ( !check_port(flow) )
return false;
- if ( !check_split_port(flow, dir) )
+ dir = check_split_port(flow, p, dir);
+ if ( dir == Binding::DR_NO_MATCH )
return false;
if ( !check_service(flow) )
return false;
+ dir = check_zone(p, dir);
+ if ( dir == Binding::DR_NO_MATCH )
+ return false;
+
return true;
}
void apply(const Stuff&, Flow*);
void set_binding(SnortConfig*, Binding*);
- void get_bindings(Flow*, Stuff&);
+ void get_bindings(Flow*, Stuff&, Packet* = nullptr); // may be null when dealing with HA flows
void apply(Flow*, Stuff&);
Inspector* find_gadget(Flow*);
int exec_handle_gadget(void*);
void Binder::eval(Packet* p)
{
+ Stuff stuff;
Flow* flow = p->flow;
- flow->iface_in = p->pkth->ingress_index;
- flow->iface_out = p->pkth->egress_index;
- Stuff stuff;
- get_bindings(flow, stuff);
+ get_bindings(flow, stuff, p);
apply(flow, stuff);
++bstats.verdicts[stuff.action];
// 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)
+void Binder::get_bindings(Flow* flow, Stuff& stuff, Packet* p)
{
Binding* pb;
unsigned i, sz = bindings.size();
{
pb = bindings[i];
- if ( !pb->check_all(flow) )
+ if ( !pb->check_all(flow, p) )
continue;
if ( !pb->use.ips_index && !pb->use.inspection_index )
if ( sub )
{
- sub->get_bindings(flow, stuff);
+ sub->get_bindings(flow, stuff, p);
return;
}
}
#include "sfip/sf_ipvar.h"
class Flow;
-
+struct Packet;
struct BindWhen
{
enum Role
bool split_ports;
PortBitSet src_ports;
PortBitSet dst_ports;
+
+ int32_t src_zone;
+ int32_t dst_zone;
};
struct BindUse
struct Binding
{
enum DirResult
- { DR_NO_MATCH, DR_ANY_MATCH, DR_CLIENT_SRC, DR_SERVER_SRC };
+ {
+ // 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 Flow*) const;
+ bool check_all(const Flow*, Packet*) const;
bool check_ips_policy(const Flow*) const;
- bool check_iface(const Flow*) const;
+ bool check_iface(const Packet*) const;
bool check_vlan(const Flow*) const;
bool check_addr(const Flow*) const;
- DirResult check_split_addr(const Flow*) const;
+ DirResult check_split_addr(const Flow*, const Packet*, const DirResult) const;
bool check_proto(const Flow*) const;
bool check_port(const Flow*) const;
- bool check_split_port(const Flow*, const DirResult) const;
+ DirResult check_split_port(const Flow*, const Packet*, const DirResult) const;
bool check_service(const Flow*) const;
+ DirResult check_zone(const Packet*, const DirResult) const;
};
#endif
{
if ((compression == CMP_GZIP) || (compression == CMP_DEFLATE))
{
- compress_stream->next_in = const_cast<z_const Bytef*>(data);
+ compress_stream->next_in = (Bytef*)data;
compress_stream->avail_in = length;
compress_stream->next_out = buffer + offset;
compress_stream->avail_out = MAX_OCTETS - offset;
static constexpr char zlib_header[2] = { 0x78, 0x01 };
inflateReset(compress_stream);
- compress_stream->next_in = (z_const Bytef*)zlib_header;
+ compress_stream->next_in = (Bytef*)zlib_header;
compress_stream->avail_in = sizeof(zlib_header);
inflate(compress_stream, Z_SYNC_FLUSH);
using namespace std;
-Binder::Binder(TableApi& t) : table_api(t), printed(false), when_ips_policy_id(-1)
+Binder::Binder(TableApi& t) : table_api(t)
{ }
Binder::~Binder()
for ( const auto& p : ports )
table_api.add_list("ports", p);
+ if ( has_src_zone() )
+ table_api.add_option("src_zone", std::stoi(when_src_zone));
+
+ if ( has_dst_zone() )
+ table_api.add_option("dst_zone", std::stoi(when_dst_zone));
+
if ( has_proto() )
table_api.add_option("proto", when_proto);
table_api.close_table(); // "binder"
}
+void Binder::set_priority(unsigned p)
+{ priority = p; }
+
+unsigned Binder::get_priority()
+{ return priority; }
+
void Binder::set_when_ips_policy_id(int id)
{ when_ips_policy_id = id; }
void Binder::add_when_port(const std::string& port)
{ ports.push_back(port); }
+void Binder::set_when_src_zone(const std::string& zone)
+{ when_src_zone = zone; }
+
+void Binder::set_when_dst_zone(const std::string& zone)
+{ when_dst_zone = zone; }
+
void Binder::clear_ports()
{ ports.clear(); }
if ( (left) > (right) ) return true; \
}
- //By priorities of options
+ // By predetermined order
+ FIRST_IF_LT(left->get_priority(), right->get_priority());
+
+ // By priorities of options
FIRST_IF_GT(left->has_ips_policy_id(), right->has_ips_policy_id())
+
+ auto left_zone_specs = left->has_src_zone() + left->has_dst_zone();
+ auto right_zone_specs = right->has_src_zone() + right->has_dst_zone();
+ FIRST_IF_GT(left_zone_specs, right_zone_specs);
+
FIRST_IF_GT(left->has_vlans(), right->has_vlans())
FIRST_IF_GT(left->has_service(), right->has_service())
FIRST_IF_GT(left->has_proto(), right->has_proto())
FIRST_IF_GT(left->has_role(), right->has_role())
- //By values of options. Fewer specs = more specific.
+ // By values of options. Fewer specs = more specific.
if ( left->has_vlans() && right->has_vlans() )
FIRST_IF_LT(left->vlans.size(), right->vlans.size())
if ( left->has_ports() && right->has_ports() )
FIRST_IF_LT(left->ports.size(), right->ports.size())
- //Sorted by value for readability if all else is equal
+ // Sorted by value for readability if all else is equal
if ( left->has_ips_policy_id() && right->has_ips_policy_id() )
FIRST_IF_LT(left->when_ips_policy_id, right->when_ips_policy_id)
void print_binder_priorities()
{
- static unsigned const num_combos = 2 << 10;
+ static unsigned const num_combos = 2 << 12;
vector<shared_ptr<Binder>> binders;
TableApi t;
binders.back()->set_when_ips_policy_id(1);
if ( i & (1 << 1) )
- binders.back()->add_when_vlan("a");
+ binders.back()->set_when_src_zone("0");
if ( i & (1 << 2) )
- binders.back()->set_when_service("a");
+ binders.back()->set_when_dst_zone("0");
if ( i & (1 << 3) )
- binders.back()->add_when_src_net("a");
+ binders.back()->add_when_vlan("a");
if ( i & (1 << 4) )
- binders.back()->add_when_dst_net("a");
+ binders.back()->set_when_service("a");
if ( i & (1 << 5) )
- binders.back()->add_when_net("a");
+ binders.back()->add_when_src_net("a");
if ( i & (1 << 6) )
- binders.back()->add_when_src_port("a");
+ binders.back()->add_when_dst_net("a");
if ( i & (1 << 7) )
- binders.back()->add_when_dst_port("a");
+ binders.back()->add_when_net("a");
if ( i & (1 << 8) )
- binders.back()->add_when_port("a");
+ binders.back()->add_when_src_port("a");
if ( i & (1 << 9) )
- binders.back()->set_when_proto("a");
+ binders.back()->add_when_dst_port("a");
if ( i & (1 << 10) )
+ binders.back()->add_when_port("a");
+
+ if ( i & (1 << 11) )
+ binders.back()->set_when_proto("a");
+
+ if ( i & (1 << 12) )
binders.back()->set_when_role("a");
}
if ( b->has_vlans() )
cout << "vlan ";
+ if ( b->has_src_zone() )
+ cout << "src_zone ";
+
+ if ( b->has_dst_zone() )
+ cout << "dst_zone ";
+
if ( b->has_service() )
cout << "service ";
#ifndef HELPERS_PPS_BINDER_H
#define HELPERS_PPS_BINDER_H
+#include <climits>
#include <memory>
#include <string>
#include <vector>
void print_binding(bool should_print)
{ printed = !should_print; }
+ void set_priority(unsigned);
+ unsigned get_priority();
+
void set_when_ips_policy_id(int);
void set_when_service(const std::string&);
void set_when_role(const std::string&);
void add_when_port(const std::string&);
void add_when_src_port(const std::string&);
void add_when_dst_port(const std::string&);
+ void set_when_src_zone(const std::string&);
+ void set_when_dst_zone(const std::string&);
void clear_ports();
int get_when_ips_policy_id() const
bool has_ports() const
{ return !ports.empty(); }
+ bool has_src_zone() const
+ { return !when_src_zone.empty(); }
+
+ bool has_dst_zone() const
+ { return !when_dst_zone.empty(); }
+
void set_use_type(const std::string& module_name);
void set_use_name(const std::string& struct_name);
void set_use_file(const std::string& file_name, IncludeType = IT_FILE);
private:
TableApi& table_api;
- bool printed; // ensures that the binding is added once,
- // by either the destructor or user
+ bool printed = false; // ensures that the binding is added once,
+ // by either the destructor or user
- int when_ips_policy_id;
+ unsigned priority = UINT_MAX;
+
+ int when_ips_policy_id = -1;
std::string when_service;
std::string when_role;
std::string when_proto;
std::vector<std::string> ports;
std::vector<std::string> src_ports;
std::vector<std::string> dst_ports;
+ std::string when_src_zone;
+ std::string when_dst_zone;
std::string use_type;
std::string use_name;
bind["http_proxy"] = &bind_http_proxy;
bind["http_server"] = &bind_http_server;
+ // FIXIT-N add when there is a way to make this play with http_inspect bindings
+ // port 80 should not be added by default. If explicitly configured and conflicting
+ // with other bindings, punt to wizard
+ bind["http_proxy"]->print_binding(false);
+
for (const auto& type : transport)
{
bind[type]->set_when_proto("tcp");
std::string ips_policy;
TRY_FIELD(action); // ignore since nap rules don't drop
- TRY_FIELD(src_zone); // FIXIT-M parse zones when implemented
+ TRY_FIELD(src_zone);
TRY_FIELD(src_net);
TRY_FIELD(src_port);
- TRY_FIELD(dst_zone); // FIXIT-M parse zones when implemented
+ TRY_FIELD(dst_zone);
TRY_FIELD(dst_net);
TRY_FIELD(dst_port);
TRY_FIELD(vlan);
auto& bind = cv.make_pending_binder(policy_id);
+ bind.set_priority(order++);
+
+ if ( src_zone != "any" )
+ bind.set_when_src_zone(src_zone);
+
if ( src_net != "any" )
bind.add_when_src_net(src_net);
if ( src_port != "any" )
bind.add_when_src_port(src_port);
+ if ( dst_zone != "any" )
+ bind.set_when_dst_zone(dst_zone);
+
if ( dst_net != "any" )
bind.add_when_dst_net(dst_net);
return true;
}
+
+private:
+ unsigned order = 0;
};
class NapSelectorState : public ConversionState