src/service_inspectors/dns/Makefile \
src/service_inspectors/ftp_telnet/Makefile \
src/service_inspectors/gtp/Makefile \
+src/service_inspectors/dnp3/Makefile \
src/service_inspectors/http_inspect/Makefile \
src/service_inspectors/imap/Makefile \
src/service_inspectors/modbus/Makefile \
* There is a bug in pps_stream_tcp.cc.. when stream_tcp: is
specified without any arguments, snort2lua doesn't convert it.
+ Same for stream_udp.
* Loses the ip list delimiters [ ]; change to ( )
* alert_fast and alert_full: output configuration includes "file = 'foo.bar'", but file is a bool
and you cannot specify an output file name in the configuration.
+* preprocessor ports option: ports <number> not supported.
==== Runtime
* The hext DAQ does not support embedded quotes in text lines (use hex
lines as a workaround).
+* Stream TCP alert squash mechanism incorrectly squashes alerts for different TCP packets.
+
arp_spoof = { }
back_orifice = { }
+dnp3 = { }
dns = { }
gtp_inspect = default_gtp
imap = { }
service_inspectors/dns/libdns.a \
service_inspectors/ftp_telnet/libftp_telnet.a \
service_inspectors/gtp/libgtp_inspect.a \
+service_inspectors/dnp3/libdnp3.a \
service_inspectors/modbus/libmodbus.a \
service_inspectors/nhttp_inspect/libnhttp_inspect.a \
service_inspectors/rpc_decode/librpc_decode.a \
add_subdirectory(back_orifice)
+add_subdirectory(dnp3)
add_subdirectory(dns)
add_subdirectory(ftp_telnet)
add_subdirectory(gtp)
if (STATIC_INSPECTORS)
set (STATIC_INSECTOR_LIBS
back_orifice
+ dnp3
dns
ftp_telnet
gtp_inspect
SUBDIRS = \
back_orifice \
+dnp3 \
dns \
ftp_telnet \
gtp \
--- /dev/null
+
+set( FILE_LIST
+ dnp3.cc
+ dnp3.h
+ dnp3_map.cc
+ dnp3_map.h
+ dnp3_module.cc
+ dnp3_module.h
+ dnp3_reassembly.cc
+ dnp3_reassembly.h
+ dnp3_paf.cc
+ dnp3_paf.h
+ ips_dnp3_data.cc
+ ips_dnp3_func.cc
+ ips_dnp3_ind.cc
+ ips_dnp3_obj.cc
+
+)
+
+if (STATIC_INSPECTORS)
+ add_library(dnp3 STATIC ${FILE_LIST})
+
+else (STATIC_INSPECTORS)
+ add_shared_library(dnp3 inspectors ${FILE_LIST})
+
+endif (STATIC_INSPECTORS)
+
--- /dev/null
+
+file_list = \
+dnp3.cc \
+dnp3.h \
+dnp3_map.cc \
+dnp3_map.h \
+dnp3_module.cc \
+dnp3_module.h \
+dnp3_reassembly.cc \
+dnp3_reassembly.h \
+dnp3_paf.cc\
+dnp3_paf.h \
+ips_dnp3_data.cc \
+ips_dnp3_func.cc \
+ips_dnp3_ind.cc \
+ips_dnp3_obj.cc
+
+
+if STATIC_INSPECTORS
+noinst_LIBRARIES = libdnp3.a
+libdnp3_a_SOURCES = $(file_list)
+else
+shlibdir = $(pkglibdir)/inspectors
+shlib_LTLIBRARIES = libdnp3.la
+libdnp3_la_CXXFLAGS = $(AM_CXXFLAGS) -DBUILDING_SO
+libdnp3_la_LDFLAGS = -export-dynamic -shared
+libdnp3_la_SOURCES = $(file_list)
+endif
+
+AM_CXXFLAGS = @AM_CXXFLAGS@
+
--- /dev/null
+This directory contains all the files related to DNP3 protocol processing.
+
+DNP3 is supported over both TCP/IP and UDP. The inspector provides anomaly
+detection and IPS rule options for matching on data, functions, indications
+and objects in the messages.
+
+The protocol aware flushing (PAF) for DNP3 identifies and flushes frames at
+link layer. The reassembly code in dnp3_reassembly.cc handles transport layer
+reassembly and generates application layer PDUs for inspection.
+For the UDP case, packets do not go through PAF and the inspector handles
+link layer decoding. Since there can be multiple application level PDUs in a
+single UDP packet, the inspector performs detection as well.
+
+Configuration of ports is handled by the binder. The default server port is
+20000. The only other DNP3 specific configuration is CRC check enable/disable
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2015 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.
+//--------------------------------------------------------------------------
+
+// dnp3.cc author Rashmi Pitre <rrp@cisco.com>
+// based on work by Ryan Jordan
+
+#include "dnp3.h"
+#include "dnp3_paf.h"
+#include "dnp3_reassembly.h"
+
+#include "events/event_queue.h"
+
+THREAD_LOCAL Dnp3Stats dnp3_stats;
+THREAD_LOCAL ProfileStats dnp3_perf_stats;
+
+Dnp3FlowData::Dnp3FlowData() : FlowData(flow_id)
+{
+}
+
+unsigned Dnp3FlowData::flow_id = 0;
+
+static dnp3_session_data_t* get_session_data(Flow* flow)
+{
+ Dnp3FlowData* fd = (Dnp3FlowData*)flow->get_application_data(
+ Dnp3FlowData::flow_id);
+
+ return fd ? &fd->dnp3_session : nullptr;
+}
+
+static dnp3_session_data_t* set_new_dnp3_session(Packet* p)
+{
+ Dnp3FlowData* fd = new Dnp3FlowData;
+
+ p->flow->set_application_data(fd);
+ return(&fd->dnp3_session);
+}
+
+const uint8_t* dnp3_get_alt_buffer(Packet* p, unsigned& len)
+{
+ dnp3_session_data_t* dnp3_sess = get_session_data(p->flow);
+ len = 0;
+ dnp3_reassembly_data_t* rdata;
+
+ if (dnp3_sess)
+ {
+ /* rdata->buffer will be the alt decode buffer.
+ This will be returned via the get_buf inspector API*/
+
+ if (dnp3_sess->direction == DNP3_CLIENT)
+ rdata = &(dnp3_sess->client_rdata);
+ else
+ rdata = &(dnp3_sess->server_rdata);
+ if (rdata->state == DNP3_REASSEMBLY_STATE__DONE)
+ {
+ len = rdata->buflen;
+ }
+ }
+ return len ? (const uint8_t*)rdata->buffer : nullptr;
+}
+
+void dnp3_reset_alt_buffer(const Packet* p)
+{
+ dnp3_session_data_t* dnp3_sess = get_session_data(p->flow);
+
+ if (dnp3_sess)
+ {
+ dnp3_reassembly_data_t* rdata;
+
+ if (dnp3_sess->direction == DNP3_CLIENT)
+ rdata = &(dnp3_sess->client_rdata);
+ else
+ rdata = &(dnp3_sess->server_rdata);
+ if (rdata->state == DNP3_REASSEMBLY_STATE__DONE)
+ dnp3_reassembly_reset(rdata);
+ }
+}
+
+static bool dnp3_process_udp(dnp3ProtoConf& config, dnp3_session_data_t* dnp3_sess, Packet* p)
+{
+ /* Possibly multiple PDUs in this UDP payload.
+ Split up and process individually. */
+
+ uint16_t bytes_processed = 0;
+ bool truncated_pdu = false;
+
+ while (bytes_processed < p->dsize)
+ {
+ uint8_t* pdu_start;
+ uint16_t user_data, num_crcs, pdu_length;
+ dnp3_link_header_t* link;
+
+ pdu_start = (uint8_t*)(p->data + bytes_processed);
+ link = (dnp3_link_header_t*)pdu_start;
+
+ /*Stop if the start bytes are not 0x0564 */
+ if ((p->dsize < bytes_processed + 2)
+ || (link->start != DNP3_START_BYTES))
+ break;
+
+ /* Alert and stop if there's not enough data to read a length */
+ if ((p->dsize - bytes_processed < (int)sizeof(dnp3_link_header_t)) ||
+ (link->len < DNP3_HEADER_REMAINDER_LEN))
+ {
+ truncated_pdu = true;
+ break;
+ }
+
+ /* Calculate the actual length of data to inspect */
+ user_data = link->len - DNP3_HEADER_REMAINDER_LEN;
+ num_crcs = 1 + (user_data/DNP3_CHUNK_SIZE) + (user_data % DNP3_CHUNK_SIZE ? 1 : 0);
+ pdu_length = DNP3_MIN_LEN + link->len + (DNP3_CRC_SIZE*num_crcs);
+
+ if (bytes_processed + pdu_length > p->dsize)
+ {
+ truncated_pdu = true;
+ break;
+ }
+
+ dnp3_full_reassembly(config,dnp3_sess, p, pdu_start,
+ pdu_length);
+
+ bytes_processed += pdu_length;
+ }
+
+ if (truncated_pdu)
+ {
+ SnortEventqAdd(GID_DNP3, DNP3_DROPPED_FRAME);
+ }
+
+ return true;
+}
+
+/* Main runtime entry point */
+
+static void process_dnp3(dnp3ProtoConf& config, Packet* p)
+{
+ if ( p->has_tcp_data() && !p->is_full_pdu() )
+ {
+ return;
+ }
+
+ /* Attempt to get a previously allocated DNP3 block. */
+ dnp3_session_data_t* dnp3_sess = get_session_data(p->flow);
+
+ if (dnp3_sess == nullptr)
+ {
+ /* Check the stream session. If it does not currently
+ * have our DNP3 data-block attached, create one.
+ */
+ dnp3_sess = set_new_dnp3_session(p);
+
+ if ( !dnp3_sess )
+ {
+ return;
+ }
+ }
+
+ /* When pipelined DNP3 PDUs appear in a single TCP segment or UDP packet,
+ the detection engine caches the results of the rule options after
+ evaluating on the first PDU. Setting this flag stops the caching. */
+ p->packet_flags |= PKT_ALLOW_MULTIPLE_DETECT;
+
+ /* Set reassembly direction */
+ if (p->packet_flags & PKT_FROM_CLIENT)
+ dnp3_sess->direction = DNP3_CLIENT;
+ else
+ dnp3_sess->direction = DNP3_SERVER;
+
+ /* Do preprocessor-specific detection stuff here */
+ if (p->has_tcp_data())
+ {
+ ++dnp3_stats.tcp_pdus;
+ /* Single PDU. PAF already split them up into separate pseudo-packets. */
+ dnp3_full_reassembly(config, dnp3_sess, p,(uint8_t*)p->data,p->dsize);
+ }
+ else if (p->has_udp_data())
+ {
+ ++dnp3_stats.udp_packets;
+ dnp3_process_udp(config, dnp3_sess, p);
+ }
+}
+
+//-------------------------------------------------------------------------
+// class stuff
+//-------------------------------------------------------------------------
+
+class Dnp3 : public Inspector
+{
+public:
+ Dnp3(dnp3ProtoConf&);
+ ~Dnp3();
+
+ void show(SnortConfig*) override;
+ void eval(Packet*) override;
+ bool get_buf(InspectionBuffer::Type, Packet*, InspectionBuffer&) override;
+ void clear(Packet*) override;
+
+ StreamSplitter* get_splitter(bool c2s) override
+ {
+ return new Dnp3Splitter(c2s);
+ }
+
+private:
+ dnp3ProtoConf config;
+};
+
+Dnp3::Dnp3(dnp3ProtoConf& pc)
+{
+ config.check_crc = pc.check_crc;
+}
+
+Dnp3::~Dnp3()
+{
+}
+
+void Dnp3::show(SnortConfig*)
+{
+ print_dnp3_conf(config);
+}
+
+void Dnp3::eval(Packet* p)
+{
+ PERF_PROFILE(dnp3_perf_stats);
+
+ assert (p->has_tcp_data() || p->has_udp_data());
+ assert(p->flow);
+
+ ++dnp3_stats.total_packets;
+
+ process_dnp3(config, p);
+}
+
+bool Dnp3::get_buf(
+ InspectionBuffer::Type ibt, Packet* p, InspectionBuffer& b)
+{
+ if ( ibt != InspectionBuffer::IBT_ALT )
+ return false;
+
+ b.data = dnp3_get_alt_buffer(p,b.len);
+
+ return (b.data != nullptr);
+}
+
+void Dnp3::clear(Packet* p)
+{
+ dnp3_reset_alt_buffer(p);
+}
+
+//-------------------------------------------------------------------------
+// api stuff
+//-------------------------------------------------------------------------
+
+static Module* mod_ctor()
+{
+ return new Dnp3Module;
+}
+
+static void mod_dtor(Module* m)
+{
+ delete m;
+}
+
+static void dnp3_init()
+{
+ Dnp3FlowData::init();
+}
+
+static Inspector* dnp3_ctor(Module* m)
+{
+ Dnp3Module* mod = (Dnp3Module*)m;
+ dnp3ProtoConf config;
+ mod->get_data(config);
+ return new Dnp3(config);
+}
+
+static void dnp3_dtor(Inspector* p)
+{
+ delete p;
+}
+
+const InspectApi dnp3_api =
+{
+ {
+ PT_INSPECTOR,
+ sizeof(InspectApi),
+ INSAPI_VERSION,
+ 0,
+ API_RESERVED,
+ API_OPTIONS,
+ DNP3_NAME,
+ DNP3_HELP,
+ mod_ctor,
+ mod_dtor
+ },
+ IT_SERVICE,
+ (uint16_t)PktType::PDU | (uint16_t)PktType::UDP,
+ nullptr, // buffers
+ "dnp3",
+ dnp3_init,
+ nullptr, // pterm
+ nullptr, // tinit
+ nullptr, // tterm
+ dnp3_ctor,
+ dnp3_dtor,
+ nullptr, // ssn
+ nullptr // reset
+};
+#ifdef BUILDING_SO
+extern const BaseApi* ips_dnp3_func;
+extern const BaseApi* ips_dnp3_ind;
+extern const BaseApi* ips_dnp3_obj;
+extern const BaseApi* ips_dnp3_data;
+
+SO_PUBLIC const BaseApi* snort_plugins[] =
+{
+ &dnp3_api.base,
+ ips_dnp3_func,
+ ips_dnp3_ind,
+ ips_dnp3_obj,
+ ips_dnp3_data,
+ nullptr
+};
+#else
+const BaseApi* sin_dnp3 = &dnp3_api.base;
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2015 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.
+//--------------------------------------------------------------------------
+
+// dnp3.h author Rashmi Pitre <rrp@cisco.com>
+// based on work by Ryan Jordan
+
+#ifndef DNP3_H
+#define DNP3_H
+
+#include "protocols/packet.h"
+#include "profiler/profiler.h"
+#include "framework/counts.h"
+
+#define DNP3_NAME "dnp3"
+#define DNP3_HELP "dnp3 inspection"
+
+#define DNP3_BAD_CRC_STR "DNP3 Link-Layer Frame contains bad CRC."
+#define DNP3_DROPPED_FRAME_STR "DNP3 Link-Layer Frame was dropped."
+#define DNP3_DROPPED_SEGMENT_STR "DNP3 Transport-Layer Segment was dropped during reassembly."
+#define DNP3_REASSEMBLY_BUFFER_CLEARED_STR \
+ "DNP3 Reassembly Buffer was cleared without reassembling a complete message."
+#define DNP3_RESERVED_ADDRESS_STR "DNP3 Link-Layer Frame uses a reserved address."
+#define DNP3_RESERVED_FUNCTION_STR "DNP3 Application-Layer Fragment uses a reserved function code."
+
+#define DNP3_BAD_CRC 1
+#define DNP3_DROPPED_FRAME 2
+#define DNP3_DROPPED_SEGMENT 3
+#define DNP3_REASSEMBLY_BUFFER_CLEARED 4
+#define DNP3_RESERVED_ADDRESS 5
+#define DNP3_RESERVED_FUNCTION 6
+
+/* Packet directions */
+#define DNP3_CLIENT 0
+#define DNP3_SERVER 1
+
+/* Session data flags */
+#define DNP3_FUNC_RULE_FIRED 0x0001
+#define DNP3_OBJ_RULE_FIRED 0x0002
+#define DNP3_IND_RULE_FIRED 0x0004
+#define DNP3_DATA_RULE_FIRED 0x0008
+
+/* DNP3 minimum length: start (2 octets) + len (1 octet) */
+#define DNP3_MIN_LEN 3
+#define DNP3_LEN_OFFSET 2
+
+/* Length of the rest of a DNP3 link-layer header: ctrl + src + dest */
+#define DNP3_HEADER_REMAINDER_LEN 5
+
+#define DNP3_BUFFER_SIZE 2048
+
+#ifdef WORDS_BIGENDIAN
+#define DNP3_MIN_RESERVED_ADDR 0xF0FF
+#define DNP3_MAX_RESERVED_ADDR 0xFBFF
+#define DNP3_START_BYTES 0x0564
+#else
+#define DNP3_MIN_RESERVED_ADDR 0xFFF0
+#define DNP3_MAX_RESERVED_ADDR 0xFFFB
+#define DNP3_START_BYTES 0x6405
+#endif
+
+#define DNP3_START_BYTE_1 0x05
+#define DNP3_START_BYTE_2 0x64
+
+#define DNP3_CHUNK_SIZE 16
+#define DNP3_CRC_SIZE 2
+
+/* Minimum length of DNP3 "len" field in order to get a transport header. */
+#define DNP3_MIN_TRANSPORT_LEN 6
+#define DNP3_MAX_TRANSPORT_LEN 250
+
+#define DNP3_TPDU_MAX 250
+#define DNP3_LPDU_MAX 292
+
+#define DNP3_TRANSPORT_FIN(x) (x & 0x80)
+#define DNP3_TRANSPORT_FIR(x) (x & 0x40)
+#define DNP3_TRANSPORT_SEQ(x) (x & 0x3F)
+
+/* Yep, the locations of FIR and FIN are switched at this layer... */
+#define DNP3_APP_FIR(x) (x & 0x80)
+#define DNP3_APP_FIN(x) (x & 0x40)
+#define DNP3_APP_SEQ(x) (x & 0x0F)
+
+#define DNP3_OK true
+#define DNP3_FAIL false
+
+struct Dnp3Stats
+{
+ PegCount total_packets;
+ PegCount udp_packets;
+ PegCount tcp_pdus;
+ PegCount dnp3_link_layer_frames;
+ PegCount dnp3_application_pdus;
+};
+
+/* DNP3 header structures */
+struct dnp3_link_header_t
+{
+ uint16_t start;
+ uint8_t len;
+ uint8_t ctrl;
+ uint16_t dest;
+ uint16_t src;
+};
+
+struct dnp3_transport_header_t
+{
+ uint8_t control;
+};
+
+struct dnp3_app_request_header_t
+{
+ uint8_t control;
+ uint8_t function;
+};
+
+struct dnp3_app_response_header_t
+{
+ uint8_t control;
+ uint8_t function;
+ uint16_t indications;
+};
+
+enum dnp3_reassembly_state_t
+{
+ DNP3_REASSEMBLY_STATE__IDLE = 0,
+ DNP3_REASSEMBLY_STATE__ASSEMBLY,
+ DNP3_REASSEMBLY_STATE__DONE
+};
+
+struct dnp3_reassembly_data_t
+{
+ uint8_t buffer[DNP3_BUFFER_SIZE];
+ uint16_t buflen;
+ dnp3_reassembly_state_t state;
+ uint8_t last_seq;
+};
+
+/* DNP3 session data */
+struct dnp3_session_data_t
+{
+ /* Fields for rule option matching. */
+ uint8_t direction;
+ uint8_t func;
+ uint8_t obj_group;
+ uint8_t obj_var;
+ uint16_t indications;
+ uint16_t flags;
+
+ /* Reassembly stuff */
+ dnp3_reassembly_data_t client_rdata;
+ dnp3_reassembly_data_t server_rdata;
+};
+
+class Dnp3FlowData : public FlowData
+{
+public:
+ Dnp3FlowData();
+
+ static void init()
+ {
+ flow_id = FlowData::get_flow_id();
+ }
+
+public:
+ static unsigned flow_id;
+ dnp3_session_data_t dnp3_session;
+};
+
+extern THREAD_LOCAL Dnp3Stats dnp3_stats;
+extern THREAD_LOCAL ProfileStats dnp3_perf_stats;
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2015 Cisco and/or its affiliates. All rights reserved.
+// Copyright (C) 2011-2013 Sourcefire, Inc.
+//
+// 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.
+//--------------------------------------------------------------------------
+
+// dnp3_map.cc author Ryan Jordan
+
+#include "dnp3_map.h"
+#include <stdlib.h>
+#include <string.h>
+
+/* Name/value pair struct */
+struct dnp3_map_t
+{
+ const char* name;
+ uint16_t value;
+};
+
+/* Mapping of name -> function code for "dnp3_func" option. */
+static dnp3_map_t func_map[] =
+{
+ { "confirm", 0 },
+ { "read", 1 },
+ { "write", 2 },
+ { "select", 3 },
+ { "operate", 4 },
+ { "direct_operate", 5 },
+ { "direct_operate_nr", 6 },
+ { "immed_freeze", 7 },
+ { "immed_freeze_nr", 8 },
+ { "freeze_clear", 9 },
+ { "freeze_clear_nr", 10 },
+ { "freeze_at_time", 11 },
+ { "freeze_at_time_nr", 12 },
+ { "cold_restart", 13 },
+ { "warm_restart", 14 },
+ { "initialize_data", 15 },
+ { "initialize_appl", 16 },
+ { "start_appl", 17 },
+ { "stop_appl", 18 },
+ { "save_config", 19 },
+ { "enable_unsolicited", 20 },
+ { "disable_unsolicited", 21 },
+ { "assign_class", 22 },
+ { "delay_measure", 23 },
+ { "record_current_time", 24 },
+ { "open_file", 25 },
+ { "close_file", 26 },
+ { "delete_file", 27 },
+ { "get_file_info", 28 },
+ { "authenticate_file", 29 },
+ { "abort_file", 30 },
+ { "activate_config", 31 },
+ { "authenticate_req", 32 },
+ { "authenticate_err", 33 },
+ { "response", 129 },
+ { "unsolicited_response", 130 },
+ { "authenticate_resp", 131 }
+};
+
+/* Mapping of name -> indication bit for "dnp3_ind" option. */
+static dnp3_map_t indication_map[] =
+{
+ /* The order is strange, but this is the order in which the spec
+ lists them. */
+ { "all_stations", 0x0100 },
+ { "class_1_events", 0x0200 },
+ { "class_2_events", 0x0400 },
+ { "class_3_events", 0x0800 },
+ { "need_time", 0x1000 },
+ { "local_control", 0x2000 },
+ { "device_trouble", 0x4000 },
+ { "device_restart", 0x8000 },
+ { "no_func_code_support", 0x0001 },
+ { "object_unknown", 0x0002 },
+ { "parameter_error", 0x0004 },
+ { "event_buffer_overflow", 0x0008 },
+ { "already_executing", 0x0010 },
+ { "config_corrupt", 0x0020 },
+ { "reserved_2", 0x0040 },
+ { "reserved_1", 0x0080 },
+};
+
+bool dnp3_func_is_defined(uint16_t code)
+{
+ size_t num_funcs = sizeof(func_map) / sizeof(func_map[0]);
+ size_t i;
+ int func_is_defined = false;
+
+ /* Check to see if code is higher than all codes in func map */
+ if (code > func_map[num_funcs-1].value)
+ return func_is_defined;
+
+ for (i = 0; i < num_funcs-1; i++)
+ {
+ /* This short-circuit check assumes that the function map remains
+ in-order. */
+ if (code <= func_map[i].value)
+ break;
+ }
+
+ if (code == func_map[i].value)
+ func_is_defined = true;
+
+ return func_is_defined;
+}
+
+int dnp3_func_str_to_code(const char* name)
+{
+ size_t num_funcs = sizeof(func_map) / sizeof(func_map[0]);
+ size_t i;
+
+ for (i = 0; i < num_funcs; i++)
+ {
+ if (strcmp(name, func_map[i].name) == 0)
+ return func_map[i].value;
+ }
+
+ return -1;
+}
+
+int dnp3_ind_str_to_code(const char* name)
+{
+ size_t num_indications = sizeof(indication_map) / sizeof(indication_map[0]);
+ size_t i;
+
+ for (i = 0; i < num_indications; i++)
+ {
+ if (strcmp(name, indication_map[i].name) == 0)
+ return indication_map[i].value;
+ }
+
+ return -1;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2015 Cisco and/or its affiliates. All rights reserved.
+// Copyright (C) 2011-2013 Sourcefire, Inc.
+//
+// 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.
+//--------------------------------------------------------------------------
+
+// dnp3_map.h author Ryan Jordan
+
+#ifndef DNP3_MAP__H
+#define DNP3_MAP__H
+
+#include <stdint.h>
+
+/* Check if "code" is in the function map.
+ *
+ * Returns: true on success, false on failure.
+ */
+bool dnp3_func_is_defined(uint16_t code);
+
+/* Return the DNP3 function code corresponding to "name".
+ *
+ * Returns: integer
+ * -1 on failure
+ */
+int dnp3_func_str_to_code(const char* name);
+
+/* Return the DNP3 indication code corresponding to "name".
+ *
+ * Returns: integer
+ * -1 on failure
+ */
+int dnp3_ind_str_to_code(const char* name);
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2015 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.
+//--------------------------------------------------------------------------
+
+// dnp3_module.cc author Rashmi Pitre <rrp@cisco.com>
+
+#include "dnp3_module.h"
+#include "dnp3.h"
+#include "main/snort_config.h"
+
+using namespace std;
+
+static const Parameter s_params[] =
+{
+ { "check_crc", Parameter::PT_BOOL, nullptr, "false",
+ "validate checksums in DNP3 link layer frames" },
+
+ { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+static const RuleMap dnp3_rules[] =
+{
+ { DNP3_BAD_CRC, DNP3_BAD_CRC_STR },
+ { DNP3_DROPPED_FRAME, DNP3_DROPPED_FRAME_STR },
+ { DNP3_DROPPED_SEGMENT, DNP3_DROPPED_SEGMENT_STR },
+ { DNP3_REASSEMBLY_BUFFER_CLEARED, DNP3_REASSEMBLY_BUFFER_CLEARED_STR },
+ { DNP3_RESERVED_ADDRESS, DNP3_RESERVED_ADDRESS_STR },
+ { DNP3_RESERVED_FUNCTION, DNP3_RESERVED_FUNCTION_STR },
+ { 0, nullptr }
+};
+
+static const PegInfo dnp3_pegs[] =
+{
+ { "total packets", "total packets" },
+ { "udp packets", "total udp packets" },
+ { "tcp pdus", "total tcp pdus" },
+ { "dnp3 link layer frames", "total dnp3 link layer frames" },
+ { "dnp3 application pdus", "total dnp3 application pdus" },
+
+ { nullptr, nullptr }
+};
+
+Dnp3Module::Dnp3Module() : Module(DNP3_NAME, DNP3_HELP, s_params)
+{
+}
+
+const RuleMap* Dnp3Module::get_rules() const
+{
+ return dnp3_rules;
+}
+
+const PegInfo* Dnp3Module::get_pegs() const
+{
+ return dnp3_pegs;
+}
+
+PegCount* Dnp3Module::get_counts() const
+{
+ return (PegCount*)&dnp3_stats;
+}
+
+ProfileStats* Dnp3Module::get_profile() const
+{
+ return &dnp3_perf_stats;
+}
+
+bool Dnp3Module::set(const char*, Value& v, SnortConfig*)
+{
+ if ( v.is("check_crc") )
+ config.check_crc = v.get_bool();
+
+ else
+ return false;
+
+ return true;
+}
+
+void Dnp3Module::get_data(dnp3ProtoConf& dnp3_config)
+{
+ dnp3_config.check_crc = config.check_crc;
+}
+
+void print_dnp3_conf(dnp3ProtoConf& config)
+{
+ LogMessage("DNP3 config: \n");
+ LogMessage(" Check CRC: %s\n",
+ config.check_crc ?
+ "ENABLED" : "DISABLED");
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2015 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.
+//--------------------------------------------------------------------------
+//
+// dnp3_module.h author Rashmi Pitre <rrp@cisco.com>
+
+#ifndef DNP3_MODULE_H
+#define DNP3_MODULE_H
+
+#include "framework/module.h"
+
+#define GID_DNP3 145
+
+struct SnortConfig;
+struct dnp3ProtoConf
+{
+ bool check_crc;
+};
+
+class Dnp3Module : public Module
+{
+public:
+ Dnp3Module();
+
+ bool set(const char*, Value&, SnortConfig*) override;
+
+ unsigned get_gid() const override
+ {
+ return GID_DNP3;
+ }
+
+ const RuleMap* get_rules() const override;
+ const PegInfo* get_pegs() const override;
+ PegCount* get_counts() const override;
+ ProfileStats* get_profile() const override;
+
+ void get_data(dnp3ProtoConf&);
+
+private:
+ dnp3ProtoConf config;
+};
+
+void print_dnp3_conf(dnp3ProtoConf& config);
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2015 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.
+//--------------------------------------------------------------------------
+
+// dnp3_paf.cc author Rashmi Pitre <rrp@cisco.com>
+// based on work by Ryan Jordan
+
+#include "dnp3_paf.h"
+#include "dnp3.h"
+
+/* Function: dnp3_paf()
+
+ Purpose: DNP3 PAF callback.
+ Statefully inspects DNP3 traffic from the start of a session,
+ Reads up until the length octet is found, then sets a flush point.
+ The flushed PDU is a DNP3 Link Layer frame, the inspector
+ handles reassembly of frames into Application Layer messages.
+
+*/
+
+static StreamSplitter::Status dnp3_paf(dnp3_paf_data* pafdata, const uint8_t* data,
+ uint32_t len, uint32_t* fp)
+{
+ //dnp3_paf_data_t *pafdata = user;
+ uint32_t bytes_processed = 0;
+
+ /* Process this packet 1 byte at a time */
+ while (bytes_processed < len)
+ {
+ uint16_t user_data = 0;
+ uint16_t num_crcs = 0;
+
+ switch (pafdata->paf_state)
+ {
+ /* Check the Start bytes. If they are not \x05\x64, don't advance state.
+ Could be out of sync, junk data between frames, mid-stream pickup, etc. */
+ case DNP3_PAF_STATE__START_1:
+ if (((uint8_t)*(data + bytes_processed)) == DNP3_START_BYTE_1)
+ pafdata->paf_state = (dnp3_paf_state)(((int)pafdata->paf_state) + 1);
+ else
+ return StreamSplitter::ABORT;
+ break;
+
+ case DNP3_PAF_STATE__START_2:
+ if (((uint8_t)*(data + bytes_processed)) == DNP3_START_BYTE_2)
+ pafdata->paf_state = (dnp3_paf_state)(((int)pafdata->paf_state) + 1);
+ else
+ return StreamSplitter::ABORT;
+ break;
+
+ /* Read the length. */
+ case DNP3_PAF_STATE__LENGTH:
+ pafdata->dnp3_length = (uint8_t)*(data + bytes_processed);
+
+ /* DNP3 length only counts non-CRC octets following the
+ length field itself. Each CRC is two octets. One follows
+ the headers, a CRC is inserted for every 16 octets of user data,
+ plus a CRC for the last bit of user data (< 16 octets) */
+
+ if (pafdata->dnp3_length < DNP3_HEADER_REMAINDER_LEN)
+ {
+ return StreamSplitter::ABORT;
+ }
+
+ user_data = pafdata->dnp3_length - DNP3_HEADER_REMAINDER_LEN;
+ num_crcs = 1 + (user_data/DNP3_CHUNK_SIZE) + (user_data % DNP3_CHUNK_SIZE ? 1 : 0);
+ pafdata->real_length = pafdata->dnp3_length + (DNP3_CRC_SIZE*num_crcs);
+
+ pafdata->paf_state = (dnp3_paf_state)(((int)pafdata->paf_state) + 1);
+ break;
+
+ /* Set the flush point. */
+ case DNP3_PAF_STATE__SET_FLUSH:
+ *fp = pafdata->real_length + bytes_processed;
+ pafdata->paf_state = DNP3_PAF_STATE__START_1;
+ return StreamSplitter::FLUSH;
+ }
+
+ bytes_processed++;
+ }
+
+ return StreamSplitter::SEARCH;
+}
+
+Dnp3Splitter::Dnp3Splitter(bool c2s) : StreamSplitter(c2s)
+{
+ state.paf_state = DNP3_PAF_STATE__START_1;
+ state.dnp3_length = 0;
+ state.real_length = 0;
+}
+
+StreamSplitter::Status Dnp3Splitter::scan(
+ Flow*, const uint8_t* data, uint32_t len,
+ uint32_t, uint32_t* fp)
+{
+ dnp3_paf_data* pfdata = &state;
+ return dnp3_paf(pfdata, data, len, fp);
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2015 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.
+//--------------------------------------------------------------------------
+
+// dnp3_paf.h author Rashmi Pitre <rrp@cisco.com>
+// based on work by Ryan Jordan
+
+#ifndef DNP3_PAF_H
+#define DNP3_PAF_H
+
+// Protocol aware flushing for DNP3.
+
+#include "main/snort_types.h"
+#include "stream/stream_api.h"
+#include "stream/stream_splitter.h"
+
+enum dnp3_paf_state
+{
+ DNP3_PAF_STATE__START_1 = 0,
+ DNP3_PAF_STATE__START_2,
+ DNP3_PAF_STATE__LENGTH,
+ DNP3_PAF_STATE__SET_FLUSH
+};
+
+struct dnp3_paf_data
+{
+ dnp3_paf_state paf_state;
+ uint8_t dnp3_length;
+ uint16_t real_length;
+};
+
+class Dnp3Splitter : public StreamSplitter
+{
+public:
+ Dnp3Splitter(bool c2s);
+
+ Status scan(Flow*, const uint8_t* data, uint32_t len,
+ uint32_t flags, uint32_t* fp) override;
+
+ bool is_paf() override
+ {
+ return true;
+ }
+
+public:
+ dnp3_paf_data state;
+};
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2015 Cisco and/or its affiliates. All rights reserved.
+// Copyright (C) 2011-2013 Sourcefire, Inc.
+//
+// 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.
+//--------------------------------------------------------------------------
+
+// dnp3_reassembly.cc author Ryan Jordan
+// modifications for snort3 by Rashmi Pitre <rrp@cisco.com>
+
+#include "dnp3_reassembly.h"
+#include "events/event_queue.h"
+#include "detection/detect.h"
+#include "dnp3_map.h"
+#include "dnp3.h"
+#include "dnp3_module.h"
+
+/* CRC look-up table, for computeCRC() below */
+static uint16_t crcLookUpTable[256] =
+{
+ 0x0000, 0x365E, 0x6CBC, 0x5AE2, 0xD978, 0xEF26, 0xB5C4, 0x839A,
+ 0xFF89, 0xC9D7, 0x9335, 0xA56B, 0x26F1, 0x10AF, 0x4A4D, 0x7C13,
+ 0xB26B, 0x8435, 0xDED7, 0xE889, 0x6B13, 0x5D4D, 0x07AF, 0x31F1,
+ 0x4DE2, 0x7BBC, 0x215E, 0x1700, 0x949A, 0xA2C4, 0xF826, 0xCE78,
+ 0x29AF, 0x1FF1, 0x4513, 0x734D, 0xF0D7, 0xC689, 0x9C6B, 0xAA35,
+ 0xD626, 0xE078, 0xBA9A, 0x8CC4, 0x0F5E, 0x3900, 0x63E2, 0x55BC,
+ 0x9BC4, 0xAD9A, 0xF778, 0xC126, 0x42BC, 0x74E2, 0x2E00, 0x185E,
+ 0x644D, 0x5213, 0x08F1, 0x3EAF, 0xBD35, 0x8B6B, 0xD189, 0xE7D7,
+ 0x535E, 0x6500, 0x3FE2, 0x09BC, 0x8A26, 0xBC78, 0xE69A, 0xD0C4,
+ 0xACD7, 0x9A89, 0xC06B, 0xF635, 0x75AF, 0x43F1, 0x1913, 0x2F4D,
+ 0xE135, 0xD76B, 0x8D89, 0xBBD7, 0x384D, 0x0E13, 0x54F1, 0x62AF,
+ 0x1EBC, 0x28E2, 0x7200, 0x445E, 0xC7C4, 0xF19A, 0xAB78, 0x9D26,
+ 0x7AF1, 0x4CAF, 0x164D, 0x2013, 0xA389, 0x95D7, 0xCF35, 0xF96B,
+ 0x8578, 0xB326, 0xE9C4, 0xDF9A, 0x5C00, 0x6A5E, 0x30BC, 0x06E2,
+ 0xC89A, 0xFEC4, 0xA426, 0x9278, 0x11E2, 0x27BC, 0x7D5E, 0x4B00,
+ 0x3713, 0x014D, 0x5BAF, 0x6DF1, 0xEE6B, 0xD835, 0x82D7, 0xB489,
+ 0xA6BC, 0x90E2, 0xCA00, 0xFC5E, 0x7FC4, 0x499A, 0x1378, 0x2526,
+ 0x5935, 0x6F6B, 0x3589, 0x03D7, 0x804D, 0xB613, 0xECF1, 0xDAAF,
+ 0x14D7, 0x2289, 0x786B, 0x4E35, 0xCDAF, 0xFBF1, 0xA113, 0x974D,
+ 0xEB5E, 0xDD00, 0x87E2, 0xB1BC, 0x3226, 0x0478, 0x5E9A, 0x68C4,
+ 0x8F13, 0xB94D, 0xE3AF, 0xD5F1, 0x566B, 0x6035, 0x3AD7, 0x0C89,
+ 0x709A, 0x46C4, 0x1C26, 0x2A78, 0xA9E2, 0x9FBC, 0xC55E, 0xF300,
+ 0x3D78, 0x0B26, 0x51C4, 0x679A, 0xE400, 0xD25E, 0x88BC, 0xBEE2,
+ 0xC2F1, 0xF4AF, 0xAE4D, 0x9813, 0x1B89, 0x2DD7, 0x7735, 0x416B,
+ 0xF5E2, 0xC3BC, 0x995E, 0xAF00, 0x2C9A, 0x1AC4, 0x4026, 0x7678,
+ 0x0A6B, 0x3C35, 0x66D7, 0x5089, 0xD313, 0xE54D, 0xBFAF, 0x89F1,
+ 0x4789, 0x71D7, 0x2B35, 0x1D6B, 0x9EF1, 0xA8AF, 0xF24D, 0xC413,
+ 0xB800, 0x8E5E, 0xD4BC, 0xE2E2, 0x6178, 0x5726, 0x0DC4, 0x3B9A,
+ 0xDC4D, 0xEA13, 0xB0F1, 0x86AF, 0x0535, 0x336B, 0x6989, 0x5FD7,
+ 0x23C4, 0x159A, 0x4F78, 0x7926, 0xFABC, 0xCCE2, 0x9600, 0xA05E,
+ 0x6E26, 0x5878, 0x029A, 0x34C4, 0xB75E, 0x8100, 0xDBE2, 0xEDBC,
+ 0x91AF, 0xA7F1, 0xFD13, 0xCB4D, 0x48D7, 0x7E89, 0x246B, 0x1235
+};
+
+/* Append a DNP3 Transport segment to the reassembly buffer.
+
+ Returns:
+ true: Segment queued successfully.
+ false: Data copy failed. Segment did not fit in reassembly buffer.
+*/
+static bool dnp3_queue_segment(dnp3_reassembly_data_t* rdata, char* buf, uint16_t buflen)
+{
+ if (rdata == NULL || buf == NULL)
+ return false;
+
+ /* We checked for DNP3_MAX_TRANSPORT_LEN earlier. */
+ if (buflen + rdata->buflen > DNP3_BUFFER_SIZE)
+ return false;
+
+ memcpy((rdata->buffer + rdata->buflen), buf, (size_t)buflen);
+
+ rdata->buflen += buflen;
+ return true;
+}
+
+/* Reset a DNP3 reassembly buffer */
+void dnp3_reassembly_reset(dnp3_reassembly_data_t* rdata)
+{
+ rdata->buflen = 0;
+ rdata->state = DNP3_REASSEMBLY_STATE__IDLE;
+ rdata->last_seq = 0;
+}
+
+/* DNP3 Transport-Layer reassembly state machine.
+
+ Arguments:
+ rdata: DNP3 reassembly state object.
+ buf: DNP3 Transport Layer segment
+ buflen: Length of Transport Layer segment.
+
+ Returns:
+ false: Segment was discarded.
+ true: Segment was queued.
+*/
+static bool dnp3_reassemble_transport(dnp3_reassembly_data_t* rdata, char* buf, uint16_t buflen)
+{
+ dnp3_transport_header_t* trans_header;
+
+ if (rdata == NULL || buf == NULL || buflen < sizeof(dnp3_transport_header_t) ||
+ (buflen > DNP3_MAX_TRANSPORT_LEN))
+ {
+ return false;
+ }
+
+ /* Take the first byte as a transport header, cut it off of the buffer. */
+ trans_header = (dnp3_transport_header_t*)buf;
+ buf += sizeof(dnp3_transport_header_t);
+ buflen -= sizeof(dnp3_transport_header_t);
+
+ /* If the previously-existing state was DONE, we need to reset it back
+ to IDLE. */
+ if (rdata->state == DNP3_REASSEMBLY_STATE__DONE)
+ dnp3_reassembly_reset(rdata);
+
+ switch (rdata->state)
+ {
+ case DNP3_REASSEMBLY_STATE__IDLE:
+ /* Discard any non-first segment. */
+ if ( DNP3_TRANSPORT_FIR(trans_header->control) == 0 )
+ return false;
+
+ /* Reset the buffer & queue the first segment */
+ dnp3_reassembly_reset(rdata);
+ dnp3_queue_segment(rdata, buf, buflen);
+ rdata->last_seq = DNP3_TRANSPORT_SEQ(trans_header->control);
+
+ if ( DNP3_TRANSPORT_FIN(trans_header->control) )
+ rdata->state = DNP3_REASSEMBLY_STATE__DONE;
+ else
+ rdata->state = DNP3_REASSEMBLY_STATE__ASSEMBLY;
+
+ break;
+
+ case DNP3_REASSEMBLY_STATE__ASSEMBLY:
+ /* Reset if the FIR flag is set. */
+ if ( DNP3_TRANSPORT_FIR(trans_header->control) )
+ {
+ dnp3_reassembly_reset(rdata);
+ dnp3_queue_segment(rdata, buf, buflen);
+ rdata->last_seq = DNP3_TRANSPORT_SEQ(trans_header->control);
+
+ if (DNP3_TRANSPORT_FIN(trans_header->control))
+ rdata->state = DNP3_REASSEMBLY_STATE__DONE;
+
+ /* Raise an alert so it's clear the buffer was reset.
+ Could signify device trouble. */
+ SnortEventqAdd(GID_DNP3, DNP3_REASSEMBLY_BUFFER_CLEARED);
+ }
+ else
+ {
+ /* Same seq but FIN is set. Discard segment, BUT finish reassembly. */
+ if ((DNP3_TRANSPORT_SEQ(trans_header->control) == rdata->last_seq) &&
+ (DNP3_TRANSPORT_FIN(trans_header->control)))
+ {
+ SnortEventqAdd(GID_DNP3, DNP3_DROPPED_SEGMENT);
+ rdata->state = DNP3_REASSEMBLY_STATE__DONE;
+ return false;
+ }
+
+ /* Discard any other segments without the correct sequence. */
+ if (DNP3_TRANSPORT_SEQ(trans_header->control) !=
+ ((rdata->last_seq + 1) % 0x40 ))
+ {
+ SnortEventqAdd(GID_DNP3, DNP3_DROPPED_SEGMENT);
+ return false;
+ }
+
+ /* Otherwise, queue it up! */
+ dnp3_queue_segment(rdata, buf, buflen);
+ rdata->last_seq = DNP3_TRANSPORT_SEQ(trans_header->control);
+
+ if (DNP3_TRANSPORT_FIN(trans_header->control))
+ rdata->state = DNP3_REASSEMBLY_STATE__DONE;
+ else
+ rdata->state = DNP3_REASSEMBLY_STATE__ASSEMBLY;
+ }
+
+ break;
+
+ case DNP3_REASSEMBLY_STATE__DONE:
+ break;
+ }
+
+ return true;
+}
+
+/* Check for reserved application-level function codes. */
+static void dnp3_check_reserved_function(dnp3_session_data_t* session)
+{
+ if ( !(dnp3_func_is_defined( (uint16_t)session->func)) )
+ {
+ SnortEventqAdd(GID_DNP3, DNP3_RESERVED_FUNCTION);
+ }
+}
+
+/* Decode a DNP3 Application-layer Fragment, fill out the relevant session data
+ for rule option evaluation. */
+static bool dnp3_process_application(dnp3_session_data_t* session)
+{
+ dnp3_reassembly_data_t* rdata = NULL;
+
+ if (session == NULL)
+ return false;
+
+ /* Master and Outstation use slightly different Application-layer headers.
+ Only the outstation sends Internal Indications. */
+ if (session->direction == DNP3_CLIENT)
+ {
+ dnp3_app_request_header_t* request = NULL;
+ rdata = &(session->client_rdata);
+
+ if (rdata->buflen < sizeof(dnp3_app_request_header_t))
+ return false;
+
+ request = (dnp3_app_request_header_t*)(rdata->buffer);
+
+ session->func = request->function;
+ }
+ else if (session->direction == DNP3_SERVER)
+ {
+ dnp3_app_response_header_t* response = NULL;
+ rdata = &(session->server_rdata);
+
+ if (rdata->buflen < sizeof(dnp3_app_response_header_t))
+ return false;
+
+ response = (dnp3_app_response_header_t*)(rdata->buffer);
+
+ session->func = response->function;
+ session->indications = ntohs(response->indications);
+ }
+
+ dnp3_check_reserved_function(session);
+
+ return true;
+}
+
+/* Check a CRC in a single block.
+ This code is mostly lifted from the example in the DNP3 spec. */
+
+static inline void compute_crc(unsigned char data, uint16_t* crcAccum)
+{
+ *crcAccum =
+ (*crcAccum >> 8) ^ crcLookUpTable[(*crcAccum ^ data) & 0xFF];
+}
+
+static bool dnp3_check_crc(unsigned char* buf, uint16_t buflen)
+{
+ uint16_t idx;
+ uint16_t crc = 0;
+
+ /* Compute check code for data in received block */
+ for (idx = 0; idx < buflen-2; idx++)
+ compute_crc(buf[idx], &crc);
+ crc = ~crc; /* Invert */
+
+ /* Check CRC at end of block */
+ if (buf[idx++] == (unsigned char)crc &&
+ buf[idx] == (unsigned char)(crc >> 8))
+ return true;
+ else
+ return false;
+}
+
+/* Check CRCs in a Link-Layer Frame, then fill a buffer containing just the user data */
+static bool dnp3_check_remove_crc(dnp3ProtoConf& config, uint8_t* pdu_start,
+ uint16_t pdu_length, char* buf, uint16_t* buflen)
+{
+ char* cursor;
+ uint16_t bytes_left;
+ uint16_t curlen = 0;
+
+ /* Check Header CRC */
+ if ((config.check_crc) &&
+ (dnp3_check_crc((unsigned char*)pdu_start, sizeof(dnp3_link_header_t)+2) == false))
+ {
+ SnortEventqAdd(GID_DNP3, DNP3_BAD_CRC);
+ return false;
+ }
+
+ cursor = (char*)pdu_start + sizeof(dnp3_link_header_t) + 2;
+ bytes_left = pdu_length - sizeof(dnp3_link_header_t) - 2;
+
+ /* Process whole 16-byte chunks (plus 2-byte CRC) */
+ while ( (bytes_left > (DNP3_CHUNK_SIZE + DNP3_CRC_SIZE)) &&
+ (curlen + DNP3_CHUNK_SIZE < *buflen) )
+ {
+ if ((config.check_crc) &&
+ (dnp3_check_crc((unsigned char*)cursor, (DNP3_CHUNK_SIZE+DNP3_CRC_SIZE)) == false))
+ {
+ SnortEventqAdd(GID_DNP3, DNP3_RESERVED_ADDRESS);
+ return false;
+ }
+
+ memcpy((buf + curlen), cursor, DNP3_CHUNK_SIZE);
+ curlen += DNP3_CHUNK_SIZE;
+ cursor += (DNP3_CHUNK_SIZE+DNP3_CRC_SIZE);
+ bytes_left -= (DNP3_CHUNK_SIZE+DNP3_CRC_SIZE);
+ }
+ /* Process leftover chunk, under 16 bytes */
+ if ( (bytes_left > DNP3_CRC_SIZE) &&
+ (curlen + bytes_left < *buflen) )
+ {
+ if ((config.check_crc) && (dnp3_check_crc((unsigned char*)cursor, bytes_left) == false))
+ {
+ SnortEventqAdd(GID_DNP3, DNP3_RESERVED_ADDRESS);
+ return false;
+ }
+
+ memcpy((buf + curlen), cursor, (bytes_left - DNP3_CRC_SIZE));
+ curlen += (bytes_left - DNP3_CRC_SIZE);
+ cursor += bytes_left;
+ bytes_left = 0;
+ }
+
+ *buflen = curlen;
+ return true;
+}
+
+static bool dnp3_check_reserved_addrs(dnp3_link_header_t* link)
+{
+ int bad_addr = 0;
+
+ if ((link->src >= DNP3_MIN_RESERVED_ADDR) && (link->src <= DNP3_MAX_RESERVED_ADDR))
+ bad_addr = 1;
+
+ else if ((link->dest >= DNP3_MIN_RESERVED_ADDR) && (link->dest <= DNP3_MAX_RESERVED_ADDR))
+ bad_addr = 1;
+
+ if (bad_addr)
+ {
+ SnortEventqAdd(GID_DNP3, DNP3_RESERVED_ADDRESS);
+ return false;
+ }
+
+ return true;
+}
+
+/* Main DNP3 Reassembly function. */
+bool dnp3_full_reassembly(dnp3ProtoConf& config, dnp3_session_data_t* session, Packet* packet,
+ uint8_t* pdu_start, uint16_t pdu_length)
+{
+ char buf[DNP3_TPDU_MAX];
+ uint16_t buflen = sizeof(buf);
+ dnp3_link_header_t* link;
+ dnp3_reassembly_data_t* rdata;
+
+ if (pdu_length < (sizeof(dnp3_link_header_t) + sizeof(dnp3_transport_header_t) + 2))
+ return false;
+
+ if ( pdu_length > DNP3_LPDU_MAX )
+ // this means PAF aborted - not DNP3
+ return false;
+
+ ++dnp3_stats.dnp3_link_layer_frames;
+
+ /* Step 1: Decode header and skip to data */
+ link = (dnp3_link_header_t*)pdu_start;
+
+ if (link->len < DNP3_MIN_TRANSPORT_LEN)
+ {
+ SnortEventqAdd(GID_DNP3, DNP3_DROPPED_FRAME);
+ return false;
+ }
+
+ /* Check reserved addresses */
+ if ( dnp3_check_reserved_addrs(link) == false )
+ return false;
+
+ /* FIXIT: NEED TO TRACK SEPARATE DNP3 SESSIONS OVER SINGLE TCP SESSION */
+
+ /* Step 2: Remove CRCs */
+ if ( dnp3_check_remove_crc(config, pdu_start, pdu_length, buf, &buflen) == false )
+ return false;
+
+ /* Step 3: Queue user data in frame for Transport-Layer reassembly */
+ if (session->direction == DNP3_CLIENT)
+ rdata = &(session->client_rdata);
+ else
+ rdata = &(session->server_rdata);
+
+ if (dnp3_reassemble_transport(rdata, buf, buflen) == false)
+ return false;
+
+ /* Step 4: Decode Application-Layer */
+ if (rdata->state == DNP3_REASSEMBLY_STATE__DONE)
+ {
+ ++dnp3_stats.dnp3_application_pdus;
+
+ bool ret = dnp3_process_application(session);
+
+ /* To support multiple PDUs in UDP, we're going to call Detect()
+ on each individual PDU. The AltDecode buffer will be provided
+ via the get_buf() inspector api */
+ if ((ret == true) && (packet->is_udp()))
+ {
+ ProfileStats dnp3_detect_perf_stats;
+ PERF_PROFILE_BLOCK(dnp3_detect_perf_stats)
+ {
+ snort_detect(packet);
+ }
+ dnp3_perf_stats.elapsed -= dnp3_detect_perf_stats.elapsed;
+
+ /* Since detection was done, reset reassembly state to avoid double alerts
+ on the last PDU */
+ dnp3_reassembly_reset(rdata);
+ }
+ else
+ return ret;
+ }
+
+ return true;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2014-2015 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.
+//--------------------------------------------------------------------------
+
+// dnp3_reassembly.h author Rashmi Pitre <rrp@cisco.com>
+// based on work by Ryan Jordan
+
+#ifndef DNP3_REASSEMBLY_H
+#define DNP3_REASSEMBLY_H
+
+#include "dnp3_module.h"
+#include "dnp3.h"
+
+bool dnp3_full_reassembly(dnp3ProtoConf& config, dnp3_session_data_t* session, Packet* p,
+ uint8_t* pdu_start, uint16_t pdu_length);
+void dnp3_reassembly_reset(dnp3_reassembly_data_t* rdata);
+
+#endif
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2015 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.
+//--------------------------------------------------------------------------
+
+// ips_dnp3_data.cc author Maya Dagon <mdagon@cisco.com>
+// based on work by Ryan Jordan
+
+#include "detection/detection_defines.h"
+#include "framework/cursor.h"
+#include "framework/ips_option.h"
+#include "framework/module.h"
+#include "hash/sfhashfcn.h"
+#include "protocols/packet.h"
+#include "profiler/profiler.h"
+
+#include "dnp3.h"
+
+#define s_name "dnp3_data"
+#define s_help \
+ "sets the cursor to dnp3 data"
+
+//-------------------------------------------------------------------------
+// DNP3 data rule option
+//-------------------------------------------------------------------------
+
+static THREAD_LOCAL ProfileStats dnp3_data_perf_stats;
+
+class Dnp3DataOption : public IpsOption
+{
+public:
+ Dnp3DataOption() : IpsOption(s_name) { }
+
+ uint32_t hash() const override;
+ bool operator==(const IpsOption&) const override;
+
+ int eval(Cursor&, Packet*) override;
+};
+
+uint32_t Dnp3DataOption::hash() const
+{
+ uint32_t a = 0, b = 0, c = 0;
+
+ mix_str(a, b, c, get_name());
+ finalize(a,b,c);
+
+ return c;
+}
+
+bool Dnp3DataOption::operator==(const IpsOption& ips) const
+{
+ return !strcmp(get_name(), ips.get_name());
+}
+
+int Dnp3DataOption::eval(Cursor& c, Packet* p)
+{
+ PERF_PROFILE(dnp3_data_perf_stats);
+
+ if ((p->has_tcp_data() && !p->is_full_pdu()) || !p->flow || !p->dsize)
+ return DETECTION_OPTION_NO_MATCH;
+
+ Dnp3FlowData* fd = (Dnp3FlowData*)p->flow->get_application_data(
+ Dnp3FlowData::flow_id);
+
+ if (!fd)
+ return DETECTION_OPTION_NO_MATCH;
+
+ dnp3_session_data_t* dnp3_session = &fd->dnp3_session;
+ dnp3_reassembly_data_t* rdata;
+
+ if (dnp3_session->direction == DNP3_CLIENT)
+ rdata = &(dnp3_session->client_rdata);
+ else
+ rdata = &(dnp3_session->server_rdata);
+
+ /* Only evaluate rules against complete Application-layer fragments */
+ if (rdata->state != DNP3_REASSEMBLY_STATE__DONE)
+ return DETECTION_OPTION_NO_MATCH;
+
+ c.set(s_name,(uint8_t*)rdata->buffer, rdata->buflen);
+
+ return DETECTION_OPTION_MATCH;
+}
+
+//-------------------------------------------------------------------------
+// module
+//-------------------------------------------------------------------------
+
+class Dnp3DataModule : public Module
+{
+public:
+ Dnp3DataModule() : Module(s_name, s_help) { }
+ ProfileStats* get_profile() const override;
+};
+
+ProfileStats* Dnp3DataModule::get_profile() const
+{
+ return &dnp3_data_perf_stats;
+}
+
+//-------------------------------------------------------------------------
+// api
+//-------------------------------------------------------------------------
+
+static Module* dnp3_data_mod_ctor()
+{
+ return new Dnp3DataModule;
+}
+
+static void dnp3_data_mod_dtor(Module* m)
+{
+ delete m;
+}
+
+static IpsOption* dnp3_data_ctor(Module*, OptTreeNode*)
+{
+ return new Dnp3DataOption;
+}
+
+static void dnp3_data_dtor(IpsOption* p)
+{
+ delete p;
+}
+
+static const IpsApi ips_api =
+{
+ {
+ PT_IPS_OPTION,
+ sizeof(IpsApi),
+ IPSAPI_VERSION,
+ 0,
+ API_RESERVED,
+ API_OPTIONS,
+ s_name,
+ s_help,
+ dnp3_data_mod_ctor,
+ dnp3_data_mod_dtor
+ },
+ OPT_TYPE_DETECTION,
+ 0, PROTO_BIT__TCP | PROTO_BIT__UDP,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ dnp3_data_ctor,
+ dnp3_data_dtor,
+ nullptr
+};
+
+const BaseApi* ips_dnp3_data = &ips_api.base;
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2015 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.
+//--------------------------------------------------------------------------
+
+// ips_dnp3_func.cc author Maya Dagon <mdagon@cisco.com>
+// based on work by Ryan Jordan
+
+#include "framework/ips_option.h"
+#include "framework/module.h"
+#include "framework/parameter.h"
+#include "detection/detect.h"
+#include "detection/detection_defines.h"
+#include "hash/sfhashfcn.h"
+#include "profiler/profiler.h"
+
+#include "dnp3.h"
+#include "dnp3_map.h"
+
+//-------------------------------------------------------------------------
+// DNP3 function code rule options
+//-------------------------------------------------------------------------
+
+#define s_name "dnp3_func"
+#define s_help \
+ "detection option to check dnp3 function code"
+
+static THREAD_LOCAL ProfileStats dnp3_func_perf_stats;
+
+class Dnp3FuncOption : public IpsOption
+{
+public:
+ Dnp3FuncOption(uint16_t v) : IpsOption(s_name)
+ { func = v; }
+
+ uint32_t hash() const override;
+ bool operator==(const IpsOption&) const override;
+ int eval(Cursor&, Packet*) override;
+
+private:
+ uint16_t func;
+};
+
+uint32_t Dnp3FuncOption::hash() const
+{
+ uint32_t a = func, b = 0, c = 0;
+
+ mix_str(a,b,c,get_name());
+ finalize(a,b,c);
+
+ return c;
+}
+
+bool Dnp3FuncOption::operator==(const IpsOption& ips) const
+{
+ if ( strcmp(get_name(), ips.get_name()) )
+ return false;
+
+ const Dnp3FuncOption& rhs = (Dnp3FuncOption&)ips;
+ return (func == rhs.func);
+}
+
+int Dnp3FuncOption::eval(Cursor&, Packet* p)
+{
+ PERF_PROFILE(dnp3_func_perf_stats);
+
+ if ((p->has_tcp_data() && !p->is_full_pdu()) || !p->flow || !p->dsize)
+ return DETECTION_OPTION_NO_MATCH;
+
+ Dnp3FlowData* fd = (Dnp3FlowData*)p->flow->get_application_data(
+ Dnp3FlowData::flow_id);
+
+ if (!fd)
+ return DETECTION_OPTION_NO_MATCH;
+
+ dnp3_session_data_t* dnp3_session = &fd->dnp3_session;
+ dnp3_reassembly_data_t* rdata;
+
+ if (dnp3_session->direction == DNP3_CLIENT)
+ rdata = &(dnp3_session->client_rdata);
+ else
+ rdata = &(dnp3_session->server_rdata);
+
+ /* Only evaluate rules against complete Application-layer fragments */
+ if (rdata->state != DNP3_REASSEMBLY_STATE__DONE)
+ return DETECTION_OPTION_NO_MATCH;
+
+ if (dnp3_session->func == func)
+ return DETECTION_OPTION_MATCH;
+
+ return DETECTION_OPTION_NO_MATCH;
+}
+
+//-------------------------------------------------------------------------
+// dnp3_func module
+//-------------------------------------------------------------------------
+
+static const Parameter s_params[] =
+{
+ { "~", Parameter::PT_STRING, nullptr, nullptr,
+ "match dnp3 function code or name" },
+
+ { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+class Dnp3FuncModule : public Module
+{
+public:
+ Dnp3FuncModule() : Module(s_name, s_help, s_params) { }
+ bool set(const char*, Value&, SnortConfig*) override;
+ ProfileStats* get_profile() const override;
+
+ uint16_t func;
+};
+
+ProfileStats* Dnp3FuncModule::get_profile() const
+{
+ return &dnp3_func_perf_stats;
+}
+
+bool Dnp3FuncModule::set(const char*, Value& v, SnortConfig*)
+{
+ if ( !v.is("~"))
+ return false;
+
+ long n;
+
+ if (v.strtol(n))
+ {
+ if ((n > 255) || (n < 0))
+ {
+ ParseError("dnp3_func requires a "
+ "number beween 0 and 255, or a valid function name.\n");
+ return false;
+ }
+ }
+ else
+ {
+ n = dnp3_func_str_to_code(v.get_string());
+ if (n == -1)
+ {
+ ParseError("dnp3_func requires a "
+ "number beween 0 and 255, or a valid function name.\n");
+ return false;
+ }
+ }
+
+ func = (uint16_t)n;
+
+ return true;
+}
+
+//-------------------------------------------------------------------------
+// dnp3_func api
+//-------------------------------------------------------------------------
+
+static Module* dnp3_func_mod_ctor()
+{
+ return new Dnp3FuncModule;
+}
+
+static void dnp3_func_mod_dtor(Module* m)
+{
+ delete m;
+}
+
+static IpsOption* dnp3_func_ctor(Module* p, OptTreeNode*)
+{
+ Dnp3FuncModule* m = (Dnp3FuncModule*)p;
+ return new Dnp3FuncOption(m->func);
+}
+
+static void dnp3_func_dtor(IpsOption* p)
+{
+ delete p;
+}
+
+static const IpsApi dnp3_func_api =
+{
+ {
+ PT_IPS_OPTION,
+ sizeof(IpsApi),
+ IPSAPI_VERSION,
+ 0,
+ API_RESERVED,
+ API_OPTIONS,
+ s_name,
+ s_help,
+ dnp3_func_mod_ctor,
+ dnp3_func_mod_dtor
+ },
+ OPT_TYPE_DETECTION,
+ 0, PROTO_BIT__TCP | PROTO_BIT__UDP,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ dnp3_func_ctor,
+ dnp3_func_dtor,
+ nullptr
+};
+
+//-------------------------------------------------------------------------
+// plugin
+//-------------------------------------------------------------------------
+
+// added to snort_plugins in dnp3.cc
+const BaseApi* ips_dnp3_func = &dnp3_func_api.base;
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2015 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.
+//--------------------------------------------------------------------------
+
+// ips_dnp3_ind.cc author Maya Dagon <mdagon@cisco.com>
+// based on work by Ryan Jordan
+
+#include "framework/ips_option.h"
+#include "framework/module.h"
+#include "framework/parameter.h"
+#include "detection/detect.h"
+#include "detection/detection_defines.h"
+#include "hash/sfhashfcn.h"
+#include "profiler/profiler.h"
+
+#include "dnp3.h"
+#include "dnp3_map.h"
+
+//-------------------------------------------------------------------------
+// DNP3 indicator flags rule options
+//-------------------------------------------------------------------------
+
+#define s_name "dnp3_ind"
+#define s_help \
+ "detection option to check dnp3 indicator flags"
+
+static THREAD_LOCAL ProfileStats dnp3_ind_perf_stats;
+
+class Dnp3IndOption : public IpsOption
+{
+public:
+ Dnp3IndOption(uint16_t v) : IpsOption(s_name)
+ { flags = v; }
+
+ uint32_t hash() const override;
+ bool operator==(const IpsOption&) const override;
+ int eval(Cursor&, Packet*) override;
+
+private:
+ uint16_t flags;
+};
+
+uint32_t Dnp3IndOption::hash() const
+{
+ uint32_t a = flags, b = 0, c = 0;
+
+ mix_str(a,b,c,get_name());
+ finalize(a,b,c);
+
+ return c;
+}
+
+bool Dnp3IndOption::operator==(const IpsOption& ips) const
+{
+ if ( strcmp(get_name(), ips.get_name()) )
+ return false;
+
+ const Dnp3IndOption& rhs = (Dnp3IndOption&)ips;
+
+ return (flags == rhs.flags);
+}
+
+int Dnp3IndOption::eval(Cursor&, Packet* p)
+{
+ PERF_PROFILE(dnp3_ind_perf_stats);
+
+ if ((p->has_tcp_data() && !p->is_full_pdu()) || !p->flow || !p->dsize)
+ return DETECTION_OPTION_NO_MATCH;
+
+ Dnp3FlowData* fd = (Dnp3FlowData*)p->flow->get_application_data(
+ Dnp3FlowData::flow_id);
+
+ if (!fd)
+ return DETECTION_OPTION_NO_MATCH;
+
+ dnp3_session_data_t* dnp3_session = &fd->dnp3_session;
+
+ /* Internal Indications only apply to DNP3 responses, not requests. */
+ if (dnp3_session->direction == DNP3_CLIENT)
+ return DETECTION_OPTION_NO_MATCH;
+
+ dnp3_reassembly_data_t* rdata = &(dnp3_session->server_rdata);
+
+ /* Only evaluate rules against complete Application-layer fragments */
+ if (rdata->state != DNP3_REASSEMBLY_STATE__DONE)
+ return DETECTION_OPTION_NO_MATCH;
+
+ if (dnp3_session->indications & flags)
+ return DETECTION_OPTION_MATCH;
+
+ return DETECTION_OPTION_NO_MATCH;
+}
+
+//-------------------------------------------------------------------------
+// dnp3_ind module
+//-------------------------------------------------------------------------
+
+static const Parameter s_params[] =
+{
+ { "~", Parameter::PT_STRING, nullptr, nullptr,
+ "match given dnp3 indicator flags" },
+
+ { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+class Dnp3IndModule : public Module
+{
+public:
+ Dnp3IndModule() : Module(s_name, s_help, s_params) { }
+
+ bool set(const char*, Value&, SnortConfig*) override;
+ ProfileStats* get_profile() const override;
+
+ uint16_t flags;
+};
+
+bool Dnp3IndModule::set(const char*, Value& v, SnortConfig*)
+{
+ if ( !v.is("~") )
+ return false;
+
+ flags = 0;
+
+ v.set_first_token();
+ std::string tok;
+
+ while ( v.get_next_token(tok) )
+ {
+ int flag;
+
+ if ( tok[0] == '"' )
+ tok.erase(0, 1);
+
+ if ( tok[tok.length()-1] == '"' )
+ tok.erase(tok.length()-1, 1);
+
+ flag = dnp3_ind_str_to_code(tok.c_str());
+ if ( flag == -1 )
+ {
+ ParseError("dnp3_ind requires a valid indication flag "
+ "name. '%s' is invalid.\n", tok.c_str());
+ return false;
+ }
+ flags |= (uint16_t)flag;
+ }
+ return true;
+}
+
+ProfileStats* Dnp3IndModule::get_profile() const
+{
+ return &dnp3_ind_perf_stats;
+}
+
+//-------------------------------------------------------------------------
+// dnp3_ind api
+//-------------------------------------------------------------------------
+
+static Module* dnp3_ind_mod_ctor()
+{
+ return new Dnp3IndModule;
+}
+
+static void dnp3_ind_mod_dtor(Module* m)
+{
+ delete m;
+}
+
+static IpsOption* dnp3_ind_ctor(Module* p, OptTreeNode*)
+{
+ Dnp3IndModule* m = (Dnp3IndModule*)p;
+ return new Dnp3IndOption(m->flags);
+}
+
+static void dnp3_ind_dtor(IpsOption* p)
+{
+ delete p;
+}
+
+static const IpsApi dnp3_ind_api =
+{
+ {
+ PT_IPS_OPTION,
+ sizeof(IpsApi),
+ IPSAPI_VERSION,
+ 0,
+ API_RESERVED,
+ API_OPTIONS,
+ s_name,
+ s_help,
+ dnp3_ind_mod_ctor,
+ dnp3_ind_mod_dtor
+ },
+ OPT_TYPE_DETECTION,
+ 0, PROTO_BIT__TCP | PROTO_BIT__UDP,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ dnp3_ind_ctor,
+ dnp3_ind_dtor,
+ nullptr
+};
+
+//-------------------------------------------------------------------------
+// plugin
+//-------------------------------------------------------------------------
+
+// added to snort_plugins in dnp3.cc
+const BaseApi* ips_dnp3_ind = &dnp3_ind_api.base;
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2015 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.
+//--------------------------------------------------------------------------
+
+// ips_dnp3_obj.cc author Maya Dagon <mdagon@cisco.com>
+// based on work by Ryan Jordan
+
+#include "framework/ips_option.h"
+#include "framework/module.h"
+#include "framework/parameter.h"
+#include "detection/detect.h"
+#include "detection/detection_defines.h"
+#include "hash/sfhashfcn.h"
+#include "profiler/profiler.h"
+
+#include "dnp3.h"
+
+//-------------------------------------------------------------------------
+// DNP3 object headers rule options
+//-------------------------------------------------------------------------
+
+#define s_name "dnp3_obj"
+#define s_help \
+ "detection option to check dnp3 object headers"
+
+/* Object decoding constants */
+#define DNP3_OBJ_HDR_MIN_LEN 3 /* group, var, qualifier */
+
+static THREAD_LOCAL ProfileStats dnp3_obj_perf_stats;
+
+static int dnp3_decode_object(uint8_t* buf, uint16_t buflen, uint8_t rule_group, uint8_t rule_var)
+{
+ uint8_t group, var;
+
+ if (buf == nullptr || buflen < DNP3_OBJ_HDR_MIN_LEN)
+ return DETECTION_OPTION_NO_MATCH;
+
+ /* Decode group */
+ group = *buf;
+ buf++;
+ buflen--;
+
+ /* Decode var */
+ var = *buf;
+ buf++;
+ buflen--;
+
+ /* Match the rule option here, quit decoding if we found the right header. */
+ if ((group == rule_group) && (var == rule_var))
+ return DETECTION_OPTION_MATCH;
+
+ return DETECTION_OPTION_NO_MATCH;
+}
+
+class Dnp3ObjOption : public IpsOption
+{
+public:
+ Dnp3ObjOption(uint8_t obj_group, uint8_t obj_var) :
+ IpsOption(s_name)
+ { group = obj_group; var = obj_var; }
+
+ uint32_t hash() const override;
+ bool operator==(const IpsOption&) const override;
+ int eval(Cursor&, Packet*) override;
+
+private:
+ uint8_t group;
+ uint8_t var;
+};
+
+uint32_t Dnp3ObjOption::hash() const
+{
+ uint32_t a = group, b = var, c = 0;
+
+ mix_str(a,b,c,get_name());
+ finalize(a,b,c);
+
+ return c;
+}
+
+bool Dnp3ObjOption::operator==(const IpsOption& ips) const
+{
+ if ( strcmp(get_name(), ips.get_name()) )
+ return false;
+
+ const Dnp3ObjOption& rhs = (Dnp3ObjOption&)ips;
+
+ return ((group == rhs.group) &&
+ (var == rhs.var));
+}
+
+int Dnp3ObjOption::eval(Cursor&, Packet* p)
+{
+ PERF_PROFILE(dnp3_obj_perf_stats);
+
+ size_t header_size;
+
+ if ((p->has_tcp_data() && !p->is_full_pdu()) || !p->flow || !p->dsize)
+ return DETECTION_OPTION_NO_MATCH;
+
+ Dnp3FlowData* fd = (Dnp3FlowData*)p->flow->get_application_data(
+ Dnp3FlowData::flow_id);
+
+ if (!fd)
+ return DETECTION_OPTION_NO_MATCH;
+
+ dnp3_session_data_t* dnp3_session = &fd->dnp3_session;
+ dnp3_reassembly_data_t* rdata;
+
+ if (dnp3_session->direction == DNP3_CLIENT)
+ {
+ rdata = &(dnp3_session->client_rdata);
+ header_size = sizeof(dnp3_app_request_header_t);
+ }
+ else
+ {
+ rdata = &(dnp3_session->server_rdata);
+ header_size = sizeof(dnp3_app_response_header_t);
+ }
+
+ /* Only evaluate rules against complete Application-layer fragments */
+ if (rdata->state != DNP3_REASSEMBLY_STATE__DONE)
+ return DETECTION_OPTION_NO_MATCH;
+
+ /* Skip over the App request/response header.
+ They are different sizes, depending on whether it is a request or response! */
+ if (rdata->buflen < header_size)
+ return DETECTION_OPTION_NO_MATCH;
+
+ uint8_t* obj_buffer = (uint8_t*)rdata->buffer + header_size;
+ uint16_t obj_buflen = rdata->buflen - header_size;
+
+ return dnp3_decode_object(obj_buffer, obj_buflen, group, var);
+}
+
+//-------------------------------------------------------------------------
+// dnp3_obj module
+//-------------------------------------------------------------------------
+
+static const Parameter s_params[] =
+{
+ { "group", Parameter::PT_INT, "0:255", "0",
+ "match given dnp3 object header group" },
+ { "var", Parameter::PT_INT, "0:255", "0",
+ "match given dnp3 object header var" },
+ { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
+class Dnp3ObjModule : public Module
+{
+public:
+ Dnp3ObjModule() : Module(s_name, s_help, s_params) { }
+
+ bool begin(const char*, int, SnortConfig*) override;
+ bool set(const char*, Value&, SnortConfig*) override;
+ ProfileStats* get_profile() const override;
+
+ uint8_t group;
+ uint8_t var;
+};
+
+bool Dnp3ObjModule::begin(const char*, int, SnortConfig*)
+{
+ group = 0;
+ var = 0;
+ return true;
+}
+
+bool Dnp3ObjModule::set(const char*, Value& v, SnortConfig*)
+{
+ if ( v.is("group") )
+ group = v.get_long();
+ else if ( v.is("var") )
+ var = v.get_long();
+
+ return true;
+}
+
+ProfileStats* Dnp3ObjModule::get_profile() const
+{
+ return &dnp3_obj_perf_stats;
+}
+
+//-------------------------------------------------------------------------
+// dnp3_obj api
+//-------------------------------------------------------------------------
+
+static Module* dnp3_obj_mod_ctor()
+{
+ return new Dnp3ObjModule;
+}
+
+static void dnp3_obj_mod_dtor(Module* m)
+{
+ delete m;
+}
+
+static IpsOption* dnp3_obj_ctor(Module* p, OptTreeNode*)
+{
+ Dnp3ObjModule* m = (Dnp3ObjModule*)p;
+ return new Dnp3ObjOption(m->group, m->var);
+}
+
+static void dnp3_obj_dtor(IpsOption* p)
+{
+ delete p;
+}
+
+static const IpsApi dnp3_obj_api =
+{
+ {
+ PT_IPS_OPTION,
+ sizeof(IpsApi),
+ IPSAPI_VERSION,
+ 0,
+ API_RESERVED,
+ API_OPTIONS,
+ s_name,
+ s_help,
+ dnp3_obj_mod_ctor,
+ dnp3_obj_mod_dtor
+ },
+ OPT_TYPE_DETECTION,
+ 0, PROTO_BIT__TCP | PROTO_BIT__UDP,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ dnp3_obj_ctor,
+ dnp3_obj_dtor,
+ nullptr
+};
+
+//-------------------------------------------------------------------------
+// plugin
+//-------------------------------------------------------------------------
+
+// added to snort_plugins in dnp3.cc
+const BaseApi* ips_dnp3_obj = &dnp3_obj_api.base;
+
extern const BaseApi* sin_ssl;
#ifdef STATIC_INSPECTORS
+extern const BaseApi* ips_dnp3_data;
+extern const BaseApi* ips_dnp3_func;
+extern const BaseApi* ips_dnp3_ind;
+extern const BaseApi* ips_dnp3_obj;
extern const BaseApi* ips_gtp_info;
extern const BaseApi* ips_gtp_type;
extern const BaseApi* ips_gtp_version;
extern const BaseApi* ips_ssl_version;
extern const BaseApi* sin_bo;
+extern const BaseApi* sin_dnp3;
extern const BaseApi* sin_dns;
extern const BaseApi* sin_ftp_client;
extern const BaseApi* sin_ftp_server;
sin_ssl,
#ifdef STATIC_INSPECTORS
+ ips_dnp3_data,
+ ips_dnp3_func,
+ ips_dnp3_ind,
+ ips_dnp3_obj,
ips_gtp_info,
ips_gtp_type,
ips_gtp_version,
ips_ssl_version,
sin_bo,
+ sin_dnp3,
sin_dns,
sin_ftp_client,
sin_ftp_server,
add_library(preprocessor_states
pps_arpspoof.cc
pps_bo.cc
+ pps_dnp3.cc
pps_frag3_engine.cc
pps_frag3_global.cc
pps_ftp_telnet.cc
libpreprocessor_states_a_SOURCES = \
pps_arpspoof.cc \
pps_bo.cc \
+pps_dnp3.cc \
pps_frag3_engine.cc \
pps_frag3_global.cc \
pps_ftp_telnet.cc \
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2015 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.
+//--------------------------------------------------------------------------
+// pps_dnp3.cc author Maya Dagon <mdagon@cisco.com>
+
+#include <sstream>
+#include <vector>
+
+#include "conversion_state.h"
+#include "helpers/s2l_util.h"
+#include "helpers/util_binder.h"
+
+namespace preprocessors
+{
+namespace
+{
+class Dnp3 : public ConversionState
+{
+public:
+ Dnp3(Converter& c) : ConversionState(c)
+ { converted_args = false; }
+
+ virtual ~Dnp3();
+ virtual bool convert(std::istringstream& data_stream);
+
+private:
+ bool converted_args;
+};
+} // namespace
+
+Dnp3::~Dnp3()
+{
+ if (converted_args)
+ return;
+
+ Binder tcp_bind(table_api);
+ tcp_bind.set_when_proto("tcp");
+ tcp_bind.add_when_port("20000");
+ tcp_bind.set_use_type("dnp3");
+
+ Binder udp_bind(table_api);
+ udp_bind.set_when_proto("udp");
+ udp_bind.add_when_port("20000");
+ udp_bind.set_use_type("dnp3");
+
+ table_api.open_table("dnp3");
+ table_api.close_table();
+}
+
+bool Dnp3::convert(std::istringstream& data_stream)
+{
+ std::string keyword;
+ bool retval = true;
+ bool ports_set = false;
+ Binder tcp_bind(table_api);
+ Binder udp_bind(table_api);
+
+ converted_args = true;
+
+ tcp_bind.set_when_proto("tcp");
+ tcp_bind.set_use_type("dnp3");
+ udp_bind.set_when_proto("udp");
+ udp_bind.set_use_type("dnp3");
+
+ table_api.open_table("dnp3");
+
+ // parse the file configuration
+ while (data_stream >> keyword)
+ {
+ bool tmpval = true;
+
+ if (!keyword.compare("disabled"))
+ {
+ table_api.add_deleted_comment("disabled");
+ }
+ else if (!keyword.compare("memcap"))
+ {
+ table_api.add_deleted_comment("memcap");
+ data_stream >> keyword;
+ }
+ else if (!keyword.compare("check_crc"))
+ {
+ table_api.add_option("check_crc", true);
+ }
+ else if (!keyword.compare("ports"))
+ {
+ std::string tmp = "";
+ table_api.add_diff_option_comment("ports", "bindings");
+
+ if ((data_stream >> keyword) && !keyword.compare("{"))
+ {
+ while (data_stream >> keyword && keyword.compare("}"))
+ {
+ ports_set = true;
+ tcp_bind.add_when_port(keyword);
+ udp_bind.add_when_port(keyword);
+ }
+ }
+ else
+ {
+ data_api.failed_conversion(data_stream, "ports <bracketed_port_list>");
+ retval = false;
+ }
+ }
+ else
+ {
+ tmpval = false;
+ }
+
+ if (!tmpval)
+ {
+ data_api.failed_conversion(data_stream, keyword);
+ retval = false;
+ }
+ }
+
+ if (!ports_set)
+ {
+ tcp_bind.add_when_port("20000");
+ udp_bind.add_when_port("20000");
+ }
+
+ return retval;
+}
+
+/**************************
+ ******* A P I ***********
+ **************************/
+
+static ConversionState* ctor(Converter& c)
+{
+ return new Dnp3(c);
+}
+
+static const ConvertMap preprocessor_dnp3 =
+{
+ "dnp3",
+ ctor,
+};
+
+const ConvertMap* dnp3_map = &preprocessor_dnp3;
+}
+
extern const ConvertMap* arpspoof_map;
extern const ConvertMap* arpspoof_host_map;
extern const ConvertMap* bo_map;
+extern const ConvertMap* dnp3_map;
extern const ConvertMap* frag3_engine_map;
extern const ConvertMap* frag3_global_map;
extern const ConvertMap* ftptelnet_map;
arpspoof_map,
arpspoof_host_map,
bo_map,
+ dnp3_map,
frag3_engine_map,
frag3_global_map,
ftptelnet_map,
add_library( rule_states
rule_base64_decode.cc
rule_content.cc
+ rule_convert_comma_list.cc
+ rule_dnp3_obj.cc
rule_file_data.cc
rule_http_encode.cc
rule_isdataat.cc
librule_states_a_SOURCES = \
rule_base64_decode.cc \
rule_content.cc \
+rule_convert_comma_list.cc \
+rule_dnp3_obj.cc \
rule_file_data.cc \
rule_http_encode.cc \
rule_isdataat.cc \
extern const ConvertMap* content_map;
extern const ConvertMap* cvs_map;
extern const ConvertMap* detection_filter_map;
+extern const ConvertMap* dnp3_data_map;
+extern const ConvertMap* dnp3_func_map;
+extern const ConvertMap* dnp3_ind_map;
+extern const ConvertMap* dnp3_obj_map;
extern const ConvertMap* dsize_map;
extern const ConvertMap* file_data_map;
extern const ConvertMap* flags_map;
content_map,
cvs_map,
detection_filter_map,
+ dnp3_data_map,
+ dnp3_func_map,
+ dnp3_ind_map,
+ dnp3_obj_map,
dsize_map,
file_data_map,
flags_map,
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2015 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.
+//--------------------------------------------------------------------------
+// rule_convert_comma_list.cc author Maya Dagon <mdagon@cisco.com>
+
+#include <sstream>
+
+#include "conversion_state.h"
+#include "helpers/converter.h"
+#include "rule_states/rule_api.h"
+#include "helpers/s2l_util.h"
+
+namespace rules
+{
+/*
+ * Convert rule option from comma list to a space sperated list in qoutes:
+ * x,y,z to "x y z"
+ */
+
+template<const std::string* rule_name>
+class CommaListRuleOption : public ConversionState
+{
+public:
+ CommaListRuleOption(Converter& c) : ConversionState(c) { }
+ virtual ~CommaListRuleOption() { }
+
+ virtual bool convert(std::istringstream& stream)
+ {
+ std::string val = util::get_rule_option_args(stream);
+
+ size_t start_pos = 0;
+ while ((start_pos = val.find(',', start_pos)) != std::string::npos)
+ {
+ val.replace(start_pos, 1, " ");
+ start_pos += 1;
+ }
+ val.insert(0,1,'"');
+ val.insert(val.end(),1,'"');
+
+ rule_api.add_option(*rule_name, val);
+ return set_next_rule_state(stream);
+ }
+};
+
+template<const std::string* rule_name>
+static ConversionState* comma_list_conversion_ctor(Converter& c)
+{
+ return new CommaListRuleOption<rule_name>(c);
+}
+
+/************************************
+ ********* DNP3 IND **************
+ ************************************/
+static const std::string dnp3_ind = "dnp3_ind";
+static const ConvertMap dnp3_ind_api =
+{
+ dnp3_ind,
+ comma_list_conversion_ctor<& dnp3_ind>,
+};
+
+const ConvertMap* dnp3_ind_map = &dnp3_ind_api;
+} // namespace rules
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2015 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.
+//--------------------------------------------------------------------------
+// rule_dnp3_obj.cc author Maya Dagon <mdagon@cisco.com>
+
+#include <sstream>
+
+#include "conversion_state.h"
+#include "helpers/converter.h"
+#include "rule_states/rule_api.h"
+#include "helpers/s2l_util.h"
+
+namespace rules
+{
+namespace
+{
+class DNP3Obj : public ConversionState
+{
+public:
+ DNP3Obj(Converter& c) : ConversionState(c) { }
+ virtual ~DNP3Obj() { }
+ virtual bool convert(std::istringstream& data);
+};
+} // namespace
+
+bool DNP3Obj::convert(std::istringstream& data)
+{
+ std::string val = util::get_rule_option_args(data);
+ std::string group = "group ";
+ std::string var = " var ";
+
+ /* convert from dnp3_obj: xxx,xxx to
+ * dnp3_obj: group xxx, var xxxx
+ */
+ val.insert(0, group);
+ size_t start_pos = val.find(',');
+ if (start_pos == std::string::npos)
+ rule_api.bad_rule(data, "dnp3_obj:expecting 2 args seperated by comma");
+ else
+ val.insert(start_pos+1, var);
+
+ rule_api.add_option("dnp3_obj", val);
+ return set_next_rule_state(data);
+}
+
+/**************************
+ ******* A P I ***********
+ **************************/
+
+static ConversionState* ctor(Converter& cv)
+{
+ return new DNP3Obj(cv);
+}
+
+static const std::string dnp3_obj = "dnp3_obj";
+static const ConvertMap dnp3_obj_api =
+{
+ dnp3_obj,
+ ctor,
+};
+
+const ConvertMap* dnp3_obj_map = &dnp3_obj_api;
+} // namespace rules
+
const ConvertMap* ssl_version_map = &rule_ssl_version;
+/************************************
+ ******* DNP3 DATA ************
+ ************************************/
+
+static const std::string dnp3_data = "dnp3_data";
+static const ConvertMap rule_dnp3_data =
+{
+ dnp3_data,
+ unchanged_rule_ctor<& dnp3_data, false>,
+};
+
+const ConvertMap* dnp3_data_map = &rule_dnp3_data;
+
+/************************************
+ ********* DNP3 FUNC **************
+ ************************************/
+
+static const std::string dnp3_func = "dnp3_func";
+static const ConvertMap rule_dnp3_func =
+{
+ dnp3_func,
+ unchanged_rule_ctor<& dnp3_func>,
+};
+
+const ConvertMap* dnp3_func_map = &rule_dnp3_func;
} // namespace rule