mirror of https://github.com/OISF/suricata
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
276 lines
9.2 KiB
C
276 lines
9.2 KiB
C
/* Copyright (C) 2015 Open Information Security Foundation
|
|
*
|
|
* You can copy, redistribute or modify this Program under the terms of
|
|
* the GNU General Public License version 2 as published by the Free
|
|
* Software Foundation.
|
|
*
|
|
* 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
|
|
* version 2 along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*/
|
|
|
|
#ifndef __APP_LAYER_DNP3_H__
|
|
#define __APP_LAYER_DNP3_H__
|
|
|
|
#include "detect-engine-state.h"
|
|
#include "util-hashlist.h"
|
|
#include "util-byte.h"
|
|
#include "rust.h"
|
|
|
|
/**
|
|
* The maximum size of a DNP3 link PDU.
|
|
*/
|
|
#define DNP3_MAX_LINK_PDU_LEN 292
|
|
|
|
/* DNP3 application request function codes. */
|
|
#define DNP3_APP_FC_CONFIRM 0x00
|
|
#define DNP3_APP_FC_READ 0x01
|
|
#define DNP3_APP_FC_WRITE 0x02
|
|
#define DNP3_APP_FC_SELECT 0x03
|
|
#define DNP3_APP_FC_OPERATE 0x04
|
|
#define DNP3_APP_FC_DIR_OPERATE 0x05
|
|
#define DNP3_APP_FC_DIR_OPERATE_NR 0x06
|
|
#define DNP3_APP_FC_FREEZE 0x07
|
|
#define DNP3_APP_FC_FREEZE_NR 0x08
|
|
#define DNP3_APP_FC_FREEZE_CLEAR 0x09
|
|
#define DNP3_APP_FC_FREEZE_CLEAR_NR 0x0a
|
|
#define DNP3_APP_FC_FREEZE_AT_TIME 0x0b
|
|
#define DNP3_APP_FC_FREEZE_AT_TIME_NR 0x0c
|
|
#define DNP3_APP_FC_COLD_RESTART 0x0d
|
|
#define DNP3_APP_FC_WARM_RESTART 0x0e
|
|
#define DNP3_APP_FC_INITIALIZE_DATA 0x0f
|
|
#define DNP3_APP_FC_INITIALIZE_APPLICATION 0x10
|
|
#define DNP3_APP_FC_START_APPLICATION 0x11
|
|
#define DNP3_APP_FC_STOP_APPLICATION 0x12
|
|
#define DNP3_APP_FC_SAVE_CONFIGURATION 0x13
|
|
#define DNP3_APP_FC_ENABLE_UNSOLICITED 0x14
|
|
#define DNP3_APP_FC_DISABLE_UNSOLICTED 0x15
|
|
#define DNP3_APP_FC_ASSIGN_CLASS 0x16
|
|
#define DNP3_APP_FC_DELAY_MEASUREMENT 0x17
|
|
#define DNP3_APP_FC_RECORD_CURRENT_TIME 0x18
|
|
#define DNP3_APP_FC_OPEN_TIME 0x19
|
|
#define DNP3_APP_FC_CLOSE_FILE 0x1a
|
|
#define DNP3_APP_FC_DELETE_FILE 0x1b
|
|
#define DNP3_APP_FC_GET_FILE_INFO 0x1c
|
|
#define DNP3_APP_FC_AUTHENTICATE_FILE 0x1d
|
|
#define DNP3_APP_FC_ABORT_FILE 0x1e
|
|
#define DNP3_APP_FC_ACTIVATE_CONFIG 0x1f
|
|
#define DNP3_APP_FC_AUTH_REQ 0x20
|
|
#define DNP3_APP_FC_AUTH_REQ_NR 0x21
|
|
|
|
/* DNP3 application response function codes. */
|
|
#define DNP3_APP_FC_RESPONSE 0x81
|
|
#define DNP3_APP_FC_UNSOLICITED_RESP 0x82
|
|
#define DNP3_APP_FC_AUTH_RESP 0x83
|
|
|
|
/* Extract fields from the link control octet. */
|
|
#define DNP3_LINK_DIR(control) (control & 0x80)
|
|
#define DNP3_LINK_PRI(control) (control & 0x40)
|
|
#define DNP3_LINK_FCB(control) (control & 0x20)
|
|
#define DNP3_LINK_FCV(control) (control & 0x10)
|
|
#define DNP3_LINK_FC(control) (control & 0x0f)
|
|
|
|
/* Extract fields from transport layer header octet. */
|
|
#define DNP3_TH_FIN(x) (x & 0x80)
|
|
#define DNP3_TH_FIR(x) (x & 0x40)
|
|
#define DNP3_TH_SEQ(x) (x & 0x3f)
|
|
|
|
/* Extract fields from the application control octet. */
|
|
#define DNP3_APP_FIR(x) (x & 0x80)
|
|
#define DNP3_APP_FIN(x) (x & 0x40)
|
|
#define DNP3_APP_CON(x) (x & 0x20)
|
|
#define DNP3_APP_UNS(x) (x & 0x10)
|
|
#define DNP3_APP_SEQ(x) (x & 0x0f)
|
|
|
|
/* DNP3 values are stored in little endian on the wire, so swapping will be
|
|
* needed on big endian architectures. */
|
|
#if __BYTE_ORDER == __BIG_ENDIAN
|
|
#define DNP3_SWAP16(x) SCByteSwap16(x)
|
|
#define DNP3_SWAP32(x) SCByteSwap32(x)
|
|
#define DNP3_SWAP64(x) SCByteSwap64(x)
|
|
#elif __BYTE_ORDER == __LITTLE_ENDIAN
|
|
#define DNP3_SWAP16(x) x
|
|
#define DNP3_SWAP32(x) x
|
|
#define DNP3_SWAP64(x) x
|
|
#endif
|
|
|
|
/* DNP3 decoder events. */
|
|
enum {
|
|
DNP3_DECODER_EVENT_FLOODED = 1,
|
|
DNP3_DECODER_EVENT_LEN_TOO_SMALL,
|
|
DNP3_DECODER_EVENT_BAD_LINK_CRC,
|
|
DNP3_DECODER_EVENT_BAD_TRANSPORT_CRC,
|
|
DNP3_DECODER_EVENT_MALFORMED,
|
|
DNP3_DECODER_EVENT_UNKNOWN_OBJECT,
|
|
};
|
|
|
|
/**
|
|
* \brief DNP3 link header.
|
|
*/
|
|
typedef struct DNP3LinkHeader_ {
|
|
uint8_t start_byte0; /**< First check byte. */
|
|
uint8_t start_byte1; /**< Second check byte. */
|
|
uint8_t len; /**< Length of PDU without CRCs. */
|
|
uint8_t control; /**< Control flags. */
|
|
uint16_t dst; /**< DNP3 destination address. */
|
|
uint16_t src; /**< DNP3 source address. */
|
|
uint16_t crc; /**< Link header CRC. */
|
|
} __attribute__((__packed__)) DNP3LinkHeader;
|
|
|
|
/**
|
|
* \brief DNP3 transport header.
|
|
*/
|
|
typedef uint8_t DNP3TransportHeader;
|
|
|
|
/**
|
|
* \brief DNP3 application header.
|
|
*/
|
|
typedef struct DNP3ApplicationHeader_ {
|
|
uint8_t control; /**< Control flags. */
|
|
uint8_t function_code; /**< Application function code. */
|
|
} __attribute__((__packed__)) DNP3ApplicationHeader;
|
|
|
|
/**
|
|
* \brief DNP3 internal indicators.
|
|
*
|
|
* Part of the application header for responses only.
|
|
*/
|
|
typedef struct DNP3InternalInd_ {
|
|
uint8_t iin1;
|
|
uint8_t iin2;
|
|
} __attribute__((__packed__)) DNP3InternalInd;
|
|
|
|
/**
|
|
* \brief A struct used for buffering incoming data prior to reassembly.
|
|
*/
|
|
typedef struct DNP3Buffer_ {
|
|
uint8_t *buffer;
|
|
size_t size;
|
|
int len;
|
|
int offset;
|
|
} DNP3Buffer;
|
|
|
|
/**
|
|
* \brief DNP3 application object header.
|
|
*/
|
|
typedef struct DNP3ObjHeader_ {
|
|
uint8_t group;
|
|
uint8_t variation;
|
|
uint8_t qualifier;
|
|
} __attribute__((packed)) DNP3ObjHeader;
|
|
|
|
/**
|
|
* \brief DNP3 object point.
|
|
*
|
|
* Each DNP3 object can have 0 or more points representing the values
|
|
* of the object.
|
|
*/
|
|
typedef struct DNP3Point_ {
|
|
uint32_t prefix; /**< Prefix value for point. */
|
|
uint32_t index; /**< Index of point. If the object is prefixed
|
|
* with an index then this will be that
|
|
* value. Otherwise this is the place the point
|
|
* was in the list of points (starting at 0). */
|
|
uint32_t size; /**< Size of point if the object prefix was a
|
|
* size. */
|
|
void *data; /**< Data for this point. */
|
|
TAILQ_ENTRY(DNP3Point_) next;
|
|
} DNP3Point;
|
|
|
|
typedef TAILQ_HEAD(DNP3PointList_, DNP3Point_) DNP3PointList;
|
|
|
|
/**
|
|
* \brief Struct to hold the list of decoded objects.
|
|
*/
|
|
typedef struct DNP3Object_ {
|
|
uint8_t group;
|
|
uint8_t variation;
|
|
uint8_t qualifier;
|
|
uint8_t prefix_code;
|
|
uint8_t range_code;
|
|
uint32_t start;
|
|
uint32_t stop;
|
|
uint32_t count;
|
|
DNP3PointList *points; /**< List of points for this object. */
|
|
|
|
TAILQ_ENTRY(DNP3Object_) next;
|
|
} DNP3Object;
|
|
|
|
typedef TAILQ_HEAD(DNP3ObjectList_, DNP3Object_) DNP3ObjectList;
|
|
|
|
/**
|
|
* \brief DNP3 transaction.
|
|
*/
|
|
typedef struct DNP3Transaction_ {
|
|
AppLayerTxData tx_data;
|
|
|
|
uint64_t tx_num; /**< Internal transaction ID. */
|
|
|
|
struct DNP3State_ *dnp3;
|
|
|
|
uint8_t has_request;
|
|
uint8_t request_done;
|
|
DNP3LinkHeader request_lh;
|
|
DNP3TransportHeader request_th;
|
|
DNP3ApplicationHeader request_ah;
|
|
uint8_t *request_buffer; /**< Reassembled request
|
|
* buffer. */
|
|
uint32_t request_buffer_len;
|
|
uint8_t request_complete; /**< Was the decode
|
|
* complete. It will not be
|
|
* complete if we hit objects
|
|
* we do not know. */
|
|
DNP3ObjectList request_objects;
|
|
|
|
uint8_t has_response;
|
|
uint8_t response_done;
|
|
DNP3LinkHeader response_lh;
|
|
DNP3TransportHeader response_th;
|
|
DNP3ApplicationHeader response_ah;
|
|
DNP3InternalInd response_iin;
|
|
uint8_t *response_buffer; /**< Reassembed response
|
|
* buffer. */
|
|
uint32_t response_buffer_len;
|
|
uint8_t response_complete; /**< Was the decode
|
|
* complete. It will not be
|
|
* complete if we hit objects
|
|
* we do not know. */
|
|
DNP3ObjectList response_objects;
|
|
|
|
TAILQ_ENTRY(DNP3Transaction_) next;
|
|
} DNP3Transaction;
|
|
|
|
TAILQ_HEAD(TxListHead, DNP3Transaction_);
|
|
|
|
/**
|
|
* \brief Per flow DNP3 state.
|
|
*/
|
|
typedef struct DNP3State_ {
|
|
TAILQ_HEAD(, DNP3Transaction_) tx_list;
|
|
DNP3Transaction *curr; /**< Current transaction. */
|
|
uint64_t transaction_max;
|
|
uint16_t events;
|
|
uint32_t unreplied; /**< Number of unreplied requests. */
|
|
uint8_t flooded; /**< Flag indicating flood. */
|
|
|
|
DNP3Buffer request_buffer; /**< Request buffer for buffering
|
|
* incomplete request PDUs received
|
|
* over TCP. */
|
|
DNP3Buffer response_buffer; /**< Response buffer for buffering
|
|
* incomplete response PDUs received
|
|
* over TCP. */
|
|
|
|
} DNP3State;
|
|
|
|
void RegisterDNP3Parsers(void);
|
|
void DNP3ParserRegisterTests(void);
|
|
int DNP3PrefixIsSize(uint8_t);
|
|
|
|
#endif /* __APP_LAYER_DNP3_H__ */
|