aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-iec104.c
diff options
context:
space:
mode:
authorChris Bontje <cbontje@gmail.com>2019-04-11 10:12:28 -0600
committerAnders Broman <a.broman58@gmail.com>2019-04-26 03:47:04 +0000
commit8bb8d5f86c81ff5aa40facbecc8cb14c5fce09cc (patch)
tree54522cd7233783e473bbb38f5af708d757860866 /epan/dissectors/packet-iec104.c
parent64a56cf7a623e0db10c7d826449001c00631c2ea (diff)
[packet-iec104.c] - Add dissection for IEC 60870-5-101 Messages
IEC 60870-5-101 is the traditional serial version of '104. The headers are different but the ASDU dissection is identical. Changes made to the '104 dissector to accommodate '101 are as follows: - Added in a new protocol dissector 'iec60870_101'. This dissector handles the '101 header and calls the ASDU dissector when required. - The existing '104acpi' dissector has been renamed to 'iec60870_104' to better align with the '101 addition - The '104asdu' protocol has been renamed to 'iec60870_asdu' in order to make it more generalized between the two variants. Updated variable names and display filter fields as needed. - 3 preferences exist in the iec60870_101 dissector to allow for configurable length of the COT, ASDU Addr and IOA fields. These are fixed their max length in '104 (2, 2 and 3 octets respectively) but are configurable in '101. - The ASDU dissector has been modified to accept a data parameter that contains the fixed/configurable lengths of COT, ASDU Addr and IOA fields. Bug: 15688 Change-Id: Ib0c918a40d24967caa8588067fa9e9a240af4ca5 Reviewed-on: https://code.wireshark.org/review/32802 Petri-Dish: Alexis La Goutte <alexis.lagoutte@gmail.com> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'epan/dissectors/packet-iec104.c')
-rw-r--r--epan/dissectors/packet-iec104.c652
1 files changed, 508 insertions, 144 deletions
diff --git a/epan/dissectors/packet-iec104.c b/epan/dissectors/packet-iec104.c
index 604ceab8e7..7b9c2b9fb3 100644
--- a/epan/dissectors/packet-iec104.c
+++ b/epan/dissectors/packet-iec104.c
@@ -1,5 +1,5 @@
/* packet-iec104.c
- * Routines for IEC-60870-5-104 (iec104) Protocol disassembly
+ * Routines for IEC-60870-5-101 & 104 Protocol disassembly
*
* Copyright (c) 2008 by Joan Ramio <joan@ramio.cat>
* Joan is a masculine catalan name. Search the Internet for Joan Pujol (alias Garbo).
@@ -24,19 +24,22 @@
#include <epan/expert.h>
#include "packet-tcp.h"
-void proto_register_iec104apci(void);
-void proto_register_iec104asdu(void);
-void proto_reg_handoff_iec104(void);
+void proto_register_iec60870_104(void);
+void proto_reg_handoff_iec60870_104(void);
-/* IEC-104 comment: Fields are little endian. */
+void proto_register_iec60870_101(void);
+void proto_reg_handoff_iec60870_101(void);
+void proto_register_iec60870_asdu(void);
+
+/* All fields are little endian. */
#define MAXS 256
-static dissector_handle_t iec104asdu_handle;
+static dissector_handle_t iec60870_asdu_handle;
/* the asdu header structure */
struct asduheader {
- guint16 Addr;
+ guint32 Addr;
guint8 OA;
guint8 TypeId;
guint8 TNCause;
@@ -46,6 +49,12 @@ struct asduheader {
guint8 DataLength;
};
+struct asdu_parms {
+ guint cot_len;
+ guint asdu_addr_len;
+ guint ioa_len;
+};
+
/* ASDU command value/status structure */
typedef struct {
gboolean OFF;
@@ -66,9 +75,10 @@ typedef struct {
#define IEC104_PORT 2404
static guint iec104_port = IEC104_PORT;
-/* Define the iec104 proto */
-static int proto_iec104apci = -1;
-static int proto_iec104asdu = -1;
+/* Define the iec101/104 protos */
+static int proto_iec60870_101 = -1;
+static int proto_iec60870_104 = -1;
+static int proto_iec60870_asdu = -1;
/* Protocol constants */
#define APCI_START 0x68
@@ -569,6 +579,10 @@ static const true_false_string tfs_local_dst = { "DST", "Local" };
static const true_false_string tfs_coi_i = { "Initialisation after change of local parameters", "Initialisation with unchanged local parameters" };
static const true_false_string tfs_adjusted_not_adjusted = { "Adjusted", "Not Adjusted" };
+static guint global_iec60870_cot_len = 1;
+static guint global_iec60870_asdu_addr_len = 1;
+static guint global_iec60870_ioa_len = 2;
+
/* Protocol fields to be filtered */
static int hf_apdulen = -1;
static int hf_apcitype = -1;
@@ -577,13 +591,13 @@ static int hf_apcitx = -1;
static int hf_apcirx = -1;
static int hf_apcidata = -1;
-static int hf_addr = -1;
+static int hf_addr = -1;
static int hf_oa = -1;
static int hf_typeid = -1;
static int hf_causetx = -1;
static int hf_nega = -1;
static int hf_test = -1;
-static int hf_ioa = -1;
+static int hf_ioa = -1;
static int hf_numix = -1;
static int hf_sq = -1;
static int hf_cp56time = -1;
@@ -666,6 +680,84 @@ static expert_field ei_iec104_short_asdu = EI_INIT;
static expert_field ei_iec104_apdu_min_len = EI_INIT;
static expert_field ei_iec104_apdu_invalid_len = EI_INIT;
+/* IEC 101 stuff */
+/* Initialize the protocol and registered fields */
+static int hf_iec60870_101_frame = -1;
+static int hf_iec60870_101_length = -1;
+static int hf_iec60870_101_num_user_octets = -1;
+static int hf_iec60870_101_ctrlfield = -1;
+static int hf_iec60870_101_ctrl_prm = -1;
+static int hf_iec60870_101_ctrl_fcb = -1;
+static int hf_iec60870_101_ctrl_fcv = -1;
+static int hf_iec60870_101_ctrl_dfc = -1;
+static int hf_iec60870_101_ctrl_func_pri_to_sec = -1;
+static int hf_iec60870_101_ctrl_func_sec_to_pri = -1;
+static int hf_iec60870_101_linkaddr = -1;
+static int hf_iec60870_101_checksum = -1;
+static int hf_iec60870_101_stopchar = -1;
+
+/* Initialize the subtree pointers */
+static gint ett_iec60870_101 = -1;
+static gint ett_iec60870_101_ctrlfield = -1;
+
+/* Frame Format */
+#define IEC101_VAR_LEN 0x68
+#define IEC101_FIXED_LEN 0x10
+#define IEC101_SINGLE_CHAR 0xE5
+
+static const value_string iec60870_101_frame_vals[] = {
+ { IEC101_VAR_LEN, "Variable Length" },
+ { IEC101_FIXED_LEN, "Fixed Length" },
+ { IEC101_SINGLE_CHAR, "Single Character" },
+ { 0, NULL }
+};
+
+static const value_string iec60870_101_ctrl_prm_values[] = {
+ { 0, "Message from Secondary (Responding) Station" },
+ { 1, "Message from Primary (Initiating) Station" },
+ { 0, NULL }
+};
+
+static const value_string iec60870_101_ctrl_func_pri_to_sec_values[] = {
+ { 0, "Reset of Remote Link" },
+ { 1, "Reset of User Process" },
+ { 2, "Reserved for Balanced Mode" },
+ { 3, "User Data" },
+ { 4, "User Data" },
+ { 5, "Reserved" },
+ { 6, "Reserved" },
+ { 7, "Reserved" },
+ { 8, "Expected Response Specifies Access Demand" },
+ { 9, "Request Status of Link" },
+ { 10, "Request User Data Class 1" },
+ { 11, "Request User Data Class 2" },
+ { 12, "Reserved" },
+ { 13, "Reserved" },
+ { 14, "Reserved" },
+ { 15, "Reserved" },
+ { 0, NULL }
+};
+
+static const value_string iec60870_101_ctrl_func_sec_to_pri_values[] = {
+ { 0, "ACK: Positive Acknowledgement" },
+ { 1, "NACK: Message Not Accepted, Link Busy" },
+ { 2, "Reserved" },
+ { 3, "Reserved" },
+ { 4, "Reserved" },
+ { 5, "Reserved" },
+ { 6, "Reserved" },
+ { 7, "Reserved" },
+ { 8, "User Data" },
+ { 9, "NACK: Requested Data not Available" },
+ { 10, "Reserved" },
+ { 11, "Status of Link" },
+ { 12, "Reserved" },
+ { 13, "Reserved" },
+ { 14, "Link Service not Functioning" },
+ { 15, "Link Service not Implemented" },
+ { 0, NULL }
+};
+
/* Misc. functions for dissection of signal values */
/* ====================================================================
@@ -752,14 +844,14 @@ static void get_CP56Time(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_heade
Information object address (Identifier)
ASDU -> Inform Object #1 -> Information object address
==================================================================== */
-static proto_item* get_InfoObjectAddress(guint32 *asdu_info_obj_addr, tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree)
+static proto_item* get_InfoObjectAddress(guint32 *asdu_info_obj_addr, tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree, guint ioa_len)
{
- proto_item* ti;
+ proto_item* ti = NULL;
- /* -------- Information object address */
- *asdu_info_obj_addr = tvb_get_letoh24(tvb, *offset);
- ti = proto_tree_add_item(iec104_header_tree, hf_ioa, tvb, *offset, 3, ENC_LITTLE_ENDIAN);
- (*offset) += 3;
+ /* Information object address */
+ /* Support both 16 and 24-bit IOA addresses */
+ ti = proto_tree_add_item_ret_uint(iec104_header_tree, hf_ioa, tvb, *offset, ioa_len, ENC_LITTLE_ENDIAN, asdu_info_obj_addr);
+ (*offset) += ioa_len;
return ti;
}
@@ -1087,7 +1179,7 @@ static void get_QOI(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tre
/* .... end Misc. functions for dissection of signal values */
-/* Find the APDU 104 (APDU=APCI+ASDU) length.
+/* Find the IEC60870-5-104 APDU (APDU=APCI+ASDU) length.
Includes possible tvb_length-1 bytes that don't form an APDU */
static guint get_iec104apdu_len(packet_info *pinfo _U_, tvbuff_t *tvb,
int offset, void *data _U_)
@@ -1107,13 +1199,15 @@ static guint get_iec104apdu_len(packet_info *pinfo _U_, tvbuff_t *tvb,
/* Is is called twice: For 'Packet List' and for 'Packet Details' */
-static int dissect_iec104asdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
+/* This dissection is shared by the IEC '101 and '104 dissectors */
+static int dissect_iec60870_asdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
guint Len = tvb_reported_length(tvb);
guint8 Bytex;
const char *cause_str;
size_t Ind;
- struct asduheader asduh;
+ struct asduheader asduh = { .OA = 0, .Addr = 0, .IOA = 0};
+ struct asdu_parms* parms = (struct asdu_parms*)data;
proto_item *it104;
proto_tree *it104tree;
wmem_strbuf_t * res;
@@ -1124,41 +1218,55 @@ static int dissect_iec104asdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre
proto_item * itSignal = NULL;
proto_tree * trSignal;
- col_set_str(pinfo->cinfo, COL_PROTOCOL, "104asdu");
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "IEC 60870-5 ASDU");
- it104 = proto_tree_add_item(tree, proto_iec104asdu, tvb, 0, -1, ENC_NA);
+ it104 = proto_tree_add_item(tree, proto_iec60870_asdu, tvb, offset, -1, ENC_NA);
it104tree = proto_item_add_subtree(it104, ett_asdu);
res = wmem_strbuf_new_label(wmem_packet_scope());
/* Type identification */
- asduh.TypeId = tvb_get_guint8(tvb, 0);
- proto_tree_add_item(it104tree, hf_typeid, tvb, 0, 1, ENC_LITTLE_ENDIAN);
+ asduh.TypeId = tvb_get_guint8(tvb, offset);
+ proto_tree_add_item(it104tree, hf_typeid, tvb, offset, 1, ENC_LITTLE_ENDIAN);
asduh.DataLength = get_TypeIdLength(asduh.TypeId);
+ offset += 1;
/* Variable structure qualifier */
Bytex = tvb_get_guint8(tvb, 1);
asduh.SQ = Bytex & F_SQ;
asduh.NumIx = Bytex & 0x7F;
- proto_tree_add_item(it104tree, hf_sq, tvb, 1, 1, ENC_LITTLE_ENDIAN);
- proto_tree_add_item(it104tree, hf_numix, tvb, 1, 1, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(it104tree, hf_sq, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(it104tree, hf_numix, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ offset += 1;
/* Cause of transmission */
- asduh.TNCause = tvb_get_guint8(tvb, 2);
- proto_tree_add_item(it104tree, hf_causetx, tvb, 2, 1, ENC_LITTLE_ENDIAN);
- proto_tree_add_item(it104tree, hf_nega, tvb, 2, 1, ENC_LITTLE_ENDIAN);
- proto_tree_add_item(it104tree, hf_test, tvb, 2, 1, ENC_LITTLE_ENDIAN);
+ asduh.TNCause = tvb_get_guint8(tvb, offset);
+ proto_tree_add_item(it104tree, hf_causetx, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(it104tree, hf_nega, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(it104tree, hf_test, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ offset += 1;
/* Originator address */
- asduh.OA = tvb_get_guint8(tvb, 3);
- proto_tree_add_item(it104tree, hf_oa, tvb, 3, 1, ENC_LITTLE_ENDIAN);
+ /* This is only present if the Cause of Tx field is 2 octets */
+ if (parms->cot_len == 2) {
+ asduh.OA = tvb_get_guint8(tvb, offset);
+ proto_tree_add_item(it104tree, hf_oa, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ offset += 1;
+ }
/* Common address of ASDU */
- asduh.Addr = tvb_get_letohs(tvb, 4);
- proto_tree_add_item(it104tree, hf_addr, tvb, 4, 2, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item_ret_uint(it104tree, hf_addr, tvb, offset, parms->asdu_addr_len, ENC_LITTLE_ENDIAN, &asduh.Addr);
+ offset += parms->asdu_addr_len;
/* Information object address */
- asduh.IOA = tvb_get_letoh24(tvb, 6);
+ /* Support both 16 and 24-bit IOA addresses */
+ /* Don't increment offset, as we'll want to be at this position later */
+ if (parms->ioa_len == 3) {
+ asduh.IOA = tvb_get_letoh24(tvb, offset);
+ }
+ else if (parms->ioa_len == 2) {
+ asduh.IOA = tvb_get_letohs(tvb, offset);
+ }
cause_str = val_to_str(asduh.TNCause & F_CAUSE, causetx_types, " <CauseTx=%u>");
@@ -1189,10 +1297,9 @@ static int dissect_iec104asdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre
/* 'ASDU Details': ROOT ITEM */
proto_item_append_text(it104, ": %s '%s'", wmem_strbuf_get_str(res),
- Len >= ASDU_HEAD_LEN ? val_to_str_const(asduh.TypeId, asdu_lngtypes, "<Unknown TypeId>") : "");
+ Len >= offset + parms->ioa_len ? val_to_str_const(asduh.TypeId, asdu_lngtypes, "<Unknown TypeId>") : "");
/* 'Signal Details': TREE */
- offset = 6; /* offset position after DUI, already stored in asduh struct */
/* -------- get signal value and status based on ASDU type id */
switch (asduh.TypeId) {
@@ -1236,7 +1343,7 @@ static int dissect_iec104asdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre
{
/* create subtree for the signal values ... */
if (i == 0 || !asduh.SQ)
- trSignal = proto_tree_add_subtree(it104tree, tvb, offset, asduh.DataLength + 3,
+ trSignal = proto_tree_add_subtree(it104tree, tvb, offset, asduh.DataLength + parms->ioa_len,
ett_asdu_objects, &itSignal, "IOA:s");
else
trSignal = proto_tree_add_subtree(it104tree, tvb, offset, asduh.DataLength,
@@ -1251,7 +1358,7 @@ static int dissect_iec104asdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre
expert_add_info(pinfo, itSignal, &ei_iec104_short_asdu);
return offset;
}
- get_InfoObjectAddress(&asdu_info_obj_addr, tvb, &offset, trSignal);
+ get_InfoObjectAddress(&asdu_info_obj_addr, tvb, &offset, trSignal, parms->ioa_len);
} else {
/* -------- following Information object address depending on SQ */
if (asduh.SQ) /* <=> SQ=1, info obj addr = startaddr++ */
@@ -1267,7 +1374,7 @@ static int dissect_iec104asdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre
expert_add_info(pinfo, itSignal, &ei_iec104_short_asdu);
return offset;
}
- get_InfoObjectAddress(&asdu_info_obj_addr, tvb, &offset, trSignal);
+ get_InfoObjectAddress(&asdu_info_obj_addr, tvb, &offset, trSignal, parms->ioa_len);
}
}
@@ -1442,7 +1549,7 @@ static int dissect_iec104asdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre
/* Is is called twice: For 'Packet List' and for 'Packet Details' */
-static int dissect_iec104apci(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
+static int dissect_iec60870_104(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
guint TcpLen = tvb_reported_length(tvb);
guint8 Start, len, type, temp8;
@@ -1451,10 +1558,11 @@ static int dissect_iec104apci(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre
proto_item *it104, *ti;
proto_tree *it104tree;
wmem_strbuf_t * res;
+ struct asdu_parms parms;
- col_set_str(pinfo->cinfo, COL_PROTOCOL, "104apci");
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "IEC 60870-5-104");
- it104 = proto_tree_add_item(tree, proto_iec104apci, tvb, 0, -1, ENC_NA);
+ it104 = proto_tree_add_item(tree, proto_iec60870_104, tvb, 0, -1, ENC_NA);
it104tree = proto_item_add_subtree(it104, ett_apci);
res = wmem_strbuf_new_label(wmem_packet_scope());
@@ -1462,6 +1570,7 @@ static int dissect_iec104apci(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre
Start = 0;
for (Off = 0; Off <= TcpLen - 2; Off++) {
Start = tvb_get_guint8(tvb, Off);
+
if (Start == APCI_START) {
if (Off > 0)
{
@@ -1526,9 +1635,14 @@ static int dissect_iec104apci(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre
proto_item_append_text(it104, ": %s", wmem_strbuf_get_str(res));
- if (type == I_TYPE)
- call_dissector(iec104asdu_handle, tvb_new_subset_length_caplen(tvb, Off + APCI_LEN, -1, len - APCI_DATA_LEN), pinfo, tree);
+ if (type == I_TYPE) {
+ /* Set the field lengths to the '104 fixed values before calling the ASDU dissection */
+ parms.cot_len = 2;
+ parms.asdu_addr_len = 2;
+ parms.ioa_len = 3;
+ call_dissector_with_data(iec60870_asdu_handle, tvb_new_subset_length_caplen(tvb, Off + APCI_LEN, -1, len - APCI_DATA_LEN), pinfo, tree, &parms);
+ }
/* Don't search more the APCI_START */
break;
}
@@ -1542,51 +1656,136 @@ static int dissect_iec104apci(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tre
return tvb_captured_length(tvb);
}
-static int dissect_iec104reas(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
+/******************************************************************************************************/
+/* Code to dissect IEC 101 Protocol packets */
+/******************************************************************************************************/
+static int
+dissect_iec60870_101(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
+{
+/* Set up structures needed to add the protocol subtree and manage it */
+ proto_item *iec101_item, *ctrlfield_item;
+ proto_tree *iec101_tree, *ctrlfield_tree;
+ guint8 frametype, ctrlfield_prm, linkaddr, data_len;
+ int offset = 0;
+ struct asdu_parms parms;
+
+ /* Make entries in Protocol column on summary display */
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "IEC 60870-5-101");
+ col_clear(pinfo->cinfo, COL_INFO);
+
+ iec101_item = proto_tree_add_item(tree, proto_iec60870_101, tvb, 0, -1, ENC_NA);
+ iec101_tree = proto_item_add_subtree(iec101_item, ett_iec60870_101);
+
+ /* Add Frame Format to Protocol Tree */
+ proto_tree_add_item(iec101_tree, hf_iec60870_101_frame, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ frametype = tvb_get_guint8(tvb, 0);
+ offset += 1;
+
+ /* If this is a single character frame, there is nothing left to do... */
+ if (frametype == IEC101_SINGLE_CHAR) {
+ return offset;
+ }
+
+ if (frametype == IEC101_VAR_LEN) {
+ proto_tree_add_item(iec101_tree, hf_iec60870_101_length, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(iec101_tree, hf_iec60870_101_num_user_octets, tvb, offset+1, 1, ENC_LITTLE_ENDIAN);
+
+ /* data_len - 2 is used as we are not including the ctrl field and link address bytes in the length passed to the asdu dissector */
+ data_len = tvb_get_guint8(tvb, offset+1) - 2;
+ proto_tree_add_item(iec101_tree, hf_iec60870_101_frame, tvb, offset+2, 1, ENC_LITTLE_ENDIAN);
+ offset += 3;
+ }
+
+ /* Fields common to both variable and fixed length frames */
+ ctrlfield_item = proto_tree_add_item(iec101_tree, hf_iec60870_101_ctrlfield, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ ctrlfield_tree = proto_item_add_subtree(ctrlfield_item, ett_iec60870_101_ctrlfield);
+
+ ctrlfield_prm = tvb_get_guint8(tvb, offset) & 0x40;
+ if (ctrlfield_prm) {
+ col_append_sep_str(pinfo->cinfo, COL_INFO, ", ", "Pri->Sec");
+ proto_tree_add_item(ctrlfield_tree, hf_iec60870_101_ctrl_prm, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(ctrlfield_tree, hf_iec60870_101_ctrl_fcb, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(ctrlfield_tree, hf_iec60870_101_ctrl_fcv, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(ctrlfield_tree, hf_iec60870_101_ctrl_func_pri_to_sec, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ }
+ else {
+ col_append_sep_str(pinfo->cinfo, COL_INFO, ", ", "Sec->Pri");
+ proto_tree_add_item(ctrlfield_tree, hf_iec60870_101_ctrl_prm, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(ctrlfield_tree, hf_iec60870_101_ctrl_dfc, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(ctrlfield_tree, hf_iec60870_101_ctrl_func_sec_to_pri, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ }
+ offset += 1;
+
+ proto_tree_add_item(iec101_tree, hf_iec60870_101_linkaddr, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ linkaddr = tvb_get_guint8(tvb, offset);
+ col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "Link Address: %d ", linkaddr);
+ offset += 1;
+
+ /* If this is a variable length frame, we need to call the ASDU dissector */
+ if (frametype == IEC101_VAR_LEN) {
+
+ /* Retrieve the user preferences */
+ parms.cot_len = global_iec60870_cot_len;
+ parms.asdu_addr_len = global_iec60870_asdu_addr_len;
+ parms.ioa_len = global_iec60870_ioa_len;
+
+ call_dissector_with_data(iec60870_asdu_handle, tvb_new_subset_length_caplen(tvb, offset, -1, data_len), pinfo, tree, &parms);
+ offset += data_len;
+ }
+
+ proto_tree_add_item(iec101_tree, hf_iec60870_101_checksum, tvb, offset, 1, ENC_LITTLE_ENDIAN);
+ proto_tree_add_item(iec101_tree, hf_iec60870_101_stopchar, tvb, offset+1, 1, ENC_LITTLE_ENDIAN);
+ offset += 2;
+
+ return offset;
+
+}
+
+static int dissect_iec60870_104_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
{
/* 5th parameter = 6 = minimum bytes received to calculate the length.
* (Not 2 in order to find more APCIs in case of 'noisy' bytes between the APCIs)
*/
tcp_dissect_pdus(tvb, pinfo, tree, TRUE, APCI_LEN,
- get_iec104apdu_len, dissect_iec104apci, data);
+ get_iec104apdu_len, dissect_iec60870_104, data);
return tvb_captured_length(tvb);
}
static void
-apply_iec104_prefs(void)
+apply_iec60870_104_prefs(void)
{
/* IEC104 uses the port preference to determine direction */
- iec104_port = prefs_get_uint_value("104apci", "tcp.port");
+ iec104_port = prefs_get_uint_value("iec60870_104", "tcp.port");
}
/* The protocol has two subprotocols: Register APCI */
void
-proto_register_iec104apci(void)
+proto_register_iec60870_104(void)
{
static hf_register_info hf_ap[] = {
{ &hf_apdulen,
- { "ApduLen", "104apci.apdulen", FT_UINT8, BASE_DEC, NULL, 0x0,
+ { "ApduLen", "iec60870_104.apdulen", FT_UINT8, BASE_DEC, NULL, 0x0,
"APDU Len", HFILL }},
{ &hf_apcitype,
- { "Type", "104apci.type", FT_UINT8, BASE_HEX, VALS(apci_types), 0x00,
+ { "Type", "iec60870_104.type", FT_UINT8, BASE_HEX, VALS(apci_types), 0x00,
"APCI type", HFILL }},
{ &hf_apciutype,
- { "UType", "104apci.utype", FT_UINT8, BASE_HEX, VALS(u_types), 0xFC,
+ { "UType", "iec60870_104.utype", FT_UINT8, BASE_HEX, VALS(u_types), 0xFC,
"Apci U type", HFILL }},
{ &hf_apcitx,
- { "Tx", "104apci.tx", FT_UINT16, BASE_DEC, NULL, 0,
+ { "Tx", "iec60870_104.tx", FT_UINT16, BASE_DEC, NULL, 0,
NULL, HFILL }},
{ &hf_apcirx,
- { "Rx", "104apci.rx", FT_UINT16, BASE_DEC, NULL, 0,
+ { "Rx", "iec60870_104.rx", FT_UINT16, BASE_DEC, NULL, 0,
NULL, HFILL }},
{ &hf_apcidata,
- { "Data", "104apci.data", FT_BYTES, BASE_NONE, NULL, 0,
+ { "Data", "iec60870_104.data", FT_BYTES, BASE_NONE, NULL, 0,
NULL, HFILL }},
};
@@ -1594,299 +1793,301 @@ proto_register_iec104apci(void)
&ett_apci,
};
- proto_iec104apci = proto_register_protocol("IEC 60870-5-104-Apci", "104apci", "104apci");
+ proto_iec60870_104 = proto_register_protocol("IEC 60870-5-104", "IEC 60870-5-104", "iec60870_104");
- proto_register_field_array(proto_iec104apci, hf_ap, array_length(hf_ap));
+ /* Provide an alias to the previous name of this dissector */
+ proto_register_alias(proto_iec60870_104, "104apci");
+
+ proto_register_field_array(proto_iec60870_104, hf_ap, array_length(hf_ap));
proto_register_subtree_array(ett_ap, array_length(ett_ap));
- prefs_register_protocol(proto_iec104apci, apply_iec104_prefs);
+ prefs_register_protocol(proto_iec60870_104, apply_iec60870_104_prefs);
}
-
-/* The protocol has two subprotocols: Register ASDU */
+/* Register ASDU dissection, shared by the '101 and '104 dissectors */
void
-proto_register_iec104asdu(void)
+proto_register_iec60870_asdu(void)
{
static hf_register_info hf_as[] = {
{ &hf_addr,
- { "Addr", "104asdu.addr", FT_UINT16, BASE_DEC, NULL, 0x0,
+ { "Addr", "iec60870_asdu.addr", FT_UINT16, BASE_DEC, NULL, 0x0,
"Common Address of Asdu", HFILL }},
{ &hf_oa,
- { "OA", "104asdu.oa", FT_UINT8, BASE_DEC, NULL, 0x0,
+ { "OA", "iec60870_asdu.oa", FT_UINT8, BASE_DEC, NULL, 0x0,
"Originator Address", HFILL }},
{ &hf_typeid,
- { "TypeId", "104asdu.typeid", FT_UINT8, BASE_DEC, VALS(asdu_types), 0x0,
+ { "TypeId", "iec60870_asdu.typeid", FT_UINT8, BASE_DEC, VALS(asdu_types), 0x0,
"Asdu Type Id", HFILL }},
{ &hf_causetx,
- { "CauseTx", "104asdu.causetx", FT_UINT8, BASE_DEC, VALS(causetx_types), F_CAUSE,
+ { "CauseTx", "iec60870_asdu.causetx", FT_UINT8, BASE_DEC, VALS(causetx_types), F_CAUSE,
"Cause of Transmision", HFILL }},
{ &hf_nega,
- { "Negative", "104asdu.nega", FT_BOOLEAN, 8, NULL, F_NEGA,
+ { "Negative", "iec60870_asdu.nega", FT_BOOLEAN, 8, NULL, F_NEGA,
NULL, HFILL }},
{ &hf_test,
- { "Test", "104asdu.test", FT_BOOLEAN, 8, NULL, F_TEST,
+ { "Test", "iec60870_asdu.test", FT_BOOLEAN, 8, NULL, F_TEST,
NULL, HFILL }},
{ &hf_ioa,
- { "IOA", "104asdu.ioa", FT_UINT24, BASE_DEC, NULL, 0x0,
+ { "IOA", "iec60870_asdu.ioa", FT_UINT24, BASE_DEC, NULL, 0x0,
"Information Object Address", HFILL }},
{ &hf_numix,
- { "NumIx", "104asdu.numix", FT_UINT8, BASE_DEC, NULL, 0x7F,
+ { "NumIx", "iec60870_asdu.numix", FT_UINT8, BASE_DEC, NULL, 0x7F,
"Number of Information Objects/Elements", HFILL }},
{ &hf_sq,
- { "SQ", "104asdu.sq", FT_BOOLEAN, 8, NULL, F_SQ,
+ { "SQ", "iec60870_asdu.sq", FT_BOOLEAN, 8, NULL, F_SQ,
"Sequence", HFILL }},
{ &hf_cp56time,
- { "CP56Time", "104asdu.cp56time", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0,
+ { "CP56Time", "iec60870_asdu.cp56time", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0,
NULL, HFILL }},
{ &hf_cp56time_ms,
- { "MS", "104asdu.cp56time.ms", FT_UINT16, BASE_DEC, NULL, 0xFFFF,
+ { "MS", "iec60870_asdu.cp56time.ms", FT_UINT16, BASE_DEC, NULL, 0xFFFF,
"CP56Time milliseconds", HFILL }},
{ &hf_cp56time_min,
- { "Min", "104asdu.cp56time.min", FT_UINT8, BASE_DEC, NULL, 0x3F,
+ { "Min", "iec60870_asdu.cp56time.min", FT_UINT8, BASE_DEC, NULL, 0x3F,
"CP56Time minutes", HFILL }},
{ &hf_cp56time_iv,
- { "IV", "104asdu.cp56time.iv", FT_BOOLEAN, 8, TFS(&tfs_invalid_valid), 0x80,
+ { "IV", "iec60870_asdu.cp56time.iv", FT_BOOLEAN, 8, TFS(&tfs_invalid_valid), 0x80,
"CP56Time invalid", HFILL }},
{ &hf_cp56time_hour,
- { "Hour", "104asdu.cp56time.hour", FT_UINT8, BASE_DEC, NULL, 0x1F,
+ { "Hour", "iec60870_asdu.cp56time.hour", FT_UINT8, BASE_DEC, NULL, 0x1F,
"CP56Time hours", HFILL }},
{ &hf_cp56time_su,
- { "SU", "104asdu.cp56time.su", FT_BOOLEAN, 8, TFS(&tfs_local_dst), 0x80,
+ { "SU", "iec60870_asdu.cp56time.su", FT_BOOLEAN, 8, TFS(&tfs_local_dst), 0x80,
"CP56Time summer time", HFILL }},
{ &hf_cp56time_day,
- { "Day", "104asdu.cp56time.day", FT_UINT8, BASE_DEC, NULL, 0x1F,
+ { "Day", "iec60870_asdu.cp56time.day", FT_UINT8, BASE_DEC, NULL, 0x1F,
"CP56Time day", HFILL }},
{ &hf_cp56time_dow,
- { "DOW", "104asdu.cp56time.dow", FT_UINT8, BASE_DEC, NULL, 0xE0,
+ { "DOW", "iec60870_asdu.cp56time.dow", FT_UINT8, BASE_DEC, NULL, 0xE0,
"CP56Time day of week", HFILL }},
{ &hf_cp56time_month,
- { "Month", "104asdu.cp56time.month", FT_UINT8, BASE_DEC, NULL, 0x0F,
+ { "Month", "iec60870_asdu.cp56time.month", FT_UINT8, BASE_DEC, NULL, 0x0F,
"CP56Time month", HFILL }},
{ &hf_cp56time_year,
- { "Year", "104asdu.cp56time.year", FT_UINT8, BASE_DEC, NULL, 0x7F,
+ { "Year", "iec60870_asdu.cp56time.year", FT_UINT8, BASE_DEC, NULL, 0x7F,
"CP56Time year", HFILL }},
{ &hf_siq,
- { "SIQ", "104asdu.siq", FT_UINT8, BASE_HEX, NULL, 0,
+ { "SIQ", "iec60870_asdu.siq", FT_UINT8, BASE_HEX, NULL, 0,
NULL, HFILL }},
{ &hf_siq_spi,
- { "SPI", "104asdu.siq.spi", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x01,
+ { "SPI", "iec60870_asdu.siq.spi", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x01,
"SIQ SPI", HFILL }},
{ &hf_siq_bl,
- { "BL", "104asdu.siq.bl", FT_BOOLEAN, 8, TFS(&tfs_blocked_not_blocked), 0x10,
+ { "BL", "iec60870_asdu.siq.bl", FT_BOOLEAN, 8, TFS(&tfs_blocked_not_blocked), 0x10,
"SIQ BL", HFILL }},
{ &hf_siq_sb,
- { "SB", "104asdu.siq.sb", FT_BOOLEAN, 8, TFS(&tfs_substituted_not_substituted), 0x20,
+ { "SB", "iec60870_asdu.siq.sb", FT_BOOLEAN, 8, TFS(&tfs_substituted_not_substituted), 0x20,
"SIQ SB", HFILL }},
{ &hf_siq_nt,
- { "NT", "104asdu.siq.nt", FT_BOOLEAN, 8, TFS(&tfs_not_topical_topical), 0x40,
+ { "NT", "iec60870_asdu.siq.nt", FT_BOOLEAN, 8, TFS(&tfs_not_topical_topical), 0x40,
"SIQ NT", HFILL }},
{ &hf_siq_iv,
- { "IV", "104asdu.siq.iv", FT_BOOLEAN, 8, TFS(&tfs_invalid_valid), 0x80,
+ { "IV", "iec60870_asdu.siq.iv", FT_BOOLEAN, 8, TFS(&tfs_invalid_valid), 0x80,
"SIQ IV", HFILL }},
{ &hf_diq,
- { "DIQ", "104asdu.diq", FT_UINT8, BASE_HEX, NULL, 0,
+ { "DIQ", "iec60870_asdu.diq", FT_UINT8, BASE_HEX, NULL, 0,
NULL, HFILL }},
{ &hf_diq_dpi,
- { "DPI", "104asdu.diq.dpi", FT_UINT8, BASE_DEC, VALS(diq_types), 0x03,
+ { "DPI", "iec60870_asdu.diq.dpi", FT_UINT8, BASE_DEC, VALS(diq_types), 0x03,
"DIQ DPI", HFILL }},
{ &hf_diq_bl,
- { "BL", "104asdu.diq.bl", FT_BOOLEAN, 8, TFS(&tfs_blocked_not_blocked), 0x10,
+ { "BL", "iec60870_asdu.diq.bl", FT_BOOLEAN, 8, TFS(&tfs_blocked_not_blocked), 0x10,
"DIQ BL", HFILL }},
{ &hf_diq_sb,
- { "SB", "104asdu.diq.sb", FT_BOOLEAN, 8, TFS(&tfs_substituted_not_substituted), 0x20,
+ { "SB", "iec60870_asdu.diq.sb", FT_BOOLEAN, 8, TFS(&tfs_substituted_not_substituted), 0x20,
"DIQ SB", HFILL }},
{ &hf_diq_nt,
- { "NT", "104asdu.diq.nt", FT_BOOLEAN, 8, TFS(&tfs_not_topical_topical), 0x40,
+ { "NT", "iec60870_asdu.diq.nt", FT_BOOLEAN, 8, TFS(&tfs_not_topical_topical), 0x40,
"DIQ NT", HFILL }},
{ &hf_diq_iv,
- { "IV", "104asdu.diq.iv", FT_BOOLEAN, 8, TFS(&tfs_invalid_valid), 0x80,
+ { "IV", "iec60870_asdu.diq.iv", FT_BOOLEAN, 8, TFS(&tfs_invalid_valid), 0x80,
"DIQ IV", HFILL }},
{ &hf_qds,
- { "QDS", "104asdu.qds", FT_UINT8, BASE_HEX, NULL, 0,
+ { "QDS", "iec60870_asdu.qds", FT_UINT8, BASE_HEX, NULL, 0,
NULL, HFILL }},
{ &hf_qds_ov,
- { "OV", "104asdu.qds.ov", FT_BOOLEAN, 8, TFS(&tfs_overflow_no_overflow), 0x01,
+ { "OV", "iec60870_asdu.qds.ov", FT_BOOLEAN, 8, TFS(&tfs_overflow_no_overflow), 0x01,
"QDS OV", HFILL }},
{ &hf_qds_bl,
- { "BL", "104asdu.qds.bl", FT_BOOLEAN, 8, TFS(&tfs_blocked_not_blocked), 0x10,
+ { "BL", "iec60870_asdu.qds.bl", FT_BOOLEAN, 8, TFS(&tfs_blocked_not_blocked), 0x10,
"QDS BL", HFILL }},
{ &hf_qds_sb,
- { "SB", "104asdu.qds.sb", FT_BOOLEAN, 8, TFS(&tfs_substituted_not_substituted), 0x20,
+ { "SB", "iec60870_asdu.qds.sb", FT_BOOLEAN, 8, TFS(&tfs_substituted_not_substituted), 0x20,
"QDS SB", HFILL }},
{ &hf_qds_nt,
- { "NT", "104asdu.qds.nt", FT_BOOLEAN, 8, TFS(&tfs_not_topical_topical), 0x40,
+ { "NT", "iec60870_asdu.qds.nt", FT_BOOLEAN, 8, TFS(&tfs_not_topical_topical), 0x40,
"QDS NT", HFILL }},
{ &hf_qds_iv,
- { "IV", "104asdu.qds.iv", FT_BOOLEAN, 8, TFS(&tfs_invalid_valid), 0x80,
+ { "IV", "iec60870_asdu.qds.iv", FT_BOOLEAN, 8, TFS(&tfs_invalid_valid), 0x80,
"QDS IV", HFILL }},
{ &hf_vti,
- { "VTI", "104asdu.vti", FT_UINT8, BASE_HEX, NULL, 0,
+ { "VTI", "iec60870_asdu.vti", FT_UINT8, BASE_HEX, NULL, 0,
NULL, HFILL }},
{ &hf_vti_v,
- { "Value", "104asdu.vti.v", FT_INT8, BASE_DEC, NULL, 0x7F,
+ { "Value", "iec60870_asdu.vti.v", FT_INT8, BASE_DEC, NULL, 0x7F,
"VTI Value", HFILL }},
{ &hf_vti_t,
- { "T", "104asdu.vti.t", FT_BOOLEAN, 8, TFS(&tfs_transient_not_transient), 0x80,
+ { "T", "iec60870_asdu.vti.t", FT_BOOLEAN, 8, TFS(&tfs_transient_not_transient), 0x80,
"VTI T", HFILL }},
{ &hf_qos,
- { "QOS", "104asdu.qos", FT_UINT8, BASE_HEX, NULL, 0,
+ { "QOS", "iec60870_asdu.qos", FT_UINT8, BASE_HEX, NULL, 0,
NULL, HFILL }},
{ &hf_qos_ql,
- { "QL", "104asdu.qos.ql", FT_UINT8, BASE_DEC, NULL, 0x7F,
+ { "QL", "iec60870_asdu.qos.ql", FT_UINT8, BASE_DEC, NULL, 0x7F,
"QOS QL", HFILL }},
{ &hf_qos_se,
- { "S/E", "104asdu.qos.se", FT_BOOLEAN, 8, TFS(&tfs_select_execute), 0x80,
+ { "S/E", "iec60870_asdu.qos.se", FT_BOOLEAN, 8, TFS(&tfs_select_execute), 0x80,
"QOS S/E", HFILL }},
{ &hf_sco,
- { "SCO", "104asdu.sco", FT_UINT8, BASE_HEX, NULL, 0,
+ { "SCO", "iec60870_asdu.sco", FT_UINT8, BASE_HEX, NULL, 0,
NULL, HFILL }},
{ &hf_sco_on,
- { "ON/OFF", "104asdu.sco.on", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x01,
+ { "ON/OFF", "iec60870_asdu.sco.on", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x01,
"SCO SCS", HFILL }},
{ &hf_sco_qu,
- { "QU", "104asdu.sco.qu", FT_UINT8, BASE_DEC, VALS(qos_qu_types), 0x7C,
+ { "QU", "iec60870_asdu.sco.qu", FT_UINT8, BASE_DEC, VALS(qos_qu_types), 0x7C,
"SCO QU", HFILL }},
{ &hf_sco_se,
- { "S/E", "104asdu.sco.se", FT_BOOLEAN, 8, TFS(&tfs_select_execute), 0x80,
+ { "S/E", "iec60870_asdu.sco.se", FT_BOOLEAN, 8, TFS(&tfs_select_execute), 0x80,
"SCO S/E", HFILL }},
{ &hf_dco,
- { "DCO", "104asdu.dco", FT_UINT8, BASE_HEX, NULL, 0,
+ { "DCO", "iec60870_asdu.dco", FT_UINT8, BASE_HEX, NULL, 0,
NULL, HFILL }},
{ &hf_dco_on,
- { "ON/OFF", "104asdu.dco.on", FT_UINT8, BASE_DEC, VALS(dco_on_types), 0x03,
+ { "ON/OFF", "iec60870_asdu.dco.on", FT_UINT8, BASE_DEC, VALS(dco_on_types), 0x03,
"DCO DCS", HFILL }},
{ &hf_dco_qu,
- { "QU", "104asdu.dco.qu", FT_UINT8, BASE_DEC, VALS(qos_qu_types), 0x7C,
+ { "QU", "iec60870_asdu.dco.qu", FT_UINT8, BASE_DEC, VALS(qos_qu_types), 0x7C,
"DCO QU", HFILL }},
{ &hf_dco_se,
- { "S/E", "104asdu.dco.se", FT_BOOLEAN, 8, TFS(&tfs_select_execute), 0x80,
+ { "S/E", "iec60870_asdu.dco.se", FT_BOOLEAN, 8, TFS(&tfs_select_execute), 0x80,
"DCO S/E", HFILL }},
{ &hf_rco,
- { "RCO", "104asdu.rco", FT_UINT8, BASE_HEX, NULL, 0,
+ { "RCO", "iec60870_asdu.rco", FT_UINT8, BASE_HEX, NULL, 0,
NULL, HFILL }},
{ &hf_rco_up,
- { "UP/DOWN", "104asdu.rco.up", FT_UINT8, BASE_DEC, VALS(rco_up_types), 0x03,
+ { "UP/DOWN", "iec60870_asdu.rco.up", FT_UINT8, BASE_DEC, VALS(rco_up_types), 0x03,
"RCO RCS", HFILL }},
{ &hf_rco_qu,
- { "QU", "104asdu.rco.qu", FT_UINT8, BASE_DEC, VALS(qos_qu_types), 0x7C,
+ { "QU", "iec60870_asdu.rco.qu", FT_UINT8, BASE_DEC, VALS(qos_qu_types), 0x7C,
"RCO QU", HFILL }},
{ &hf_rco_se,
- { "S/E", "104asdu.rco.se", FT_BOOLEAN, 8, TFS(&tfs_select_execute), 0x80,
+ { "S/E", "iec60870_asdu.rco.se", FT_BOOLEAN, 8, TFS(&tfs_select_execute), 0x80,
"RCO S/E", HFILL }},
{ &hf_coi,
- { "COI", "104asdu.coi", FT_UINT8, BASE_HEX, NULL, 0,
+ { "COI", "iec60870_asdu.coi", FT_UINT8, BASE_HEX, NULL, 0,
NULL, HFILL }},
{ &hf_coi_r,
- { "R", "104asdu.coi_r", FT_UINT8, BASE_DEC, VALS(coi_r_types), 0x7F,
+ { "R", "iec60870_asdu.coi_r", FT_UINT8, BASE_DEC, VALS(coi_r_types), 0x7F,
"COI R", HFILL }},
{ &hf_coi_i,
- { "I", "104asdu.coi_i", FT_BOOLEAN, 8, TFS(&tfs_coi_i), 0x80,
+ { "I", "iec60870_asdu.coi_i", FT_BOOLEAN, 8, TFS(&tfs_coi_i), 0x80,
"COI I", HFILL }},
{ &hf_qoi,
- { "QOI", "104asdu.qoi", FT_UINT8, BASE_DEC, VALS(qoi_r_types), 0,
+ { "QOI", "iec60870_asdu.qoi", FT_UINT8, BASE_DEC, VALS(qoi_r_types), 0,
NULL, HFILL }},
{ &hf_bcr_count,
- { "Binary Counter", "104asdu.bcr.count", FT_INT32, BASE_DEC, NULL, 0x0,
+ { "Binary Counter", "iec60870_asdu.bcr.count", FT_INT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_bcr_sq,
- { "SQ", "104asdu.bcr.sq", FT_UINT8, BASE_DEC, NULL, 0x1F,
+ { "SQ", "iec60870_asdu.bcr.sq", FT_UINT8, BASE_DEC, NULL, 0x1F,
"Sequence Number", HFILL }},
{ &hf_bcr_cy,
- { "CY", "104asdu.bcr.cy", FT_BOOLEAN, 8, TFS(&tfs_overflow_no_overflow), 0x20,
+ { "CY", "iec60870_asdu.bcr.cy", FT_BOOLEAN, 8, TFS(&tfs_overflow_no_overflow), 0x20,
"Counter Overflow", HFILL }},
{ &hf_bcr_ca,
- { "CA", "104asdu.bcr.ca", FT_BOOLEAN, 8, TFS(&tfs_adjusted_not_adjusted), 0x40,
+ { "CA", "iec60870_asdu.bcr.ca", FT_BOOLEAN, 8, TFS(&tfs_adjusted_not_adjusted), 0x40,
"Counter Adjusted", HFILL }},
{ &hf_bcr_iv,
- { "IV", "104asdu.bcr.iv", FT_BOOLEAN, 8, TFS(&tfs_invalid_valid), 0x80,
+ { "IV", "iec60870_asdu.bcr.iv", FT_BOOLEAN, 8, TFS(&tfs_invalid_valid), 0x80,
"Counter Validity", HFILL }},
{ &hf_start,
- { "START", "104asdu.start", FT_UINT8, BASE_HEX, NULL, 0x0,
+ { "START", "iec60870_asdu.start", FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL }},
{ &hf_asdu_bitstring,
- { "Value", "104asdu.bitstring", FT_UINT32, BASE_HEX, NULL, 0x0,
+ { "Value", "iec60870_asdu.bitstring", FT_UINT32, BASE_HEX, NULL, 0x0,
"BSI value", HFILL }},
{ &hf_asdu_float,
- { "Value", "104asdu.float", FT_FLOAT, BASE_NONE, NULL, 0x0,
+ { "Value", "iec60870_asdu.float", FT_FLOAT, BASE_NONE, NULL, 0x0,
"Float value", HFILL }},
{ &hf_asdu_normval,
- { "Value", "104asdu.normval", FT_FLOAT, BASE_NONE, NULL, 0x0,
+ { "Value", "iec60870_asdu.normval", FT_FLOAT, BASE_NONE, NULL, 0x0,
"Normalised value", HFILL }},
{ &hf_asdu_scalval,
- { "Value", "104asdu.scalval", FT_INT16, BASE_DEC, NULL, 0x0,
+ { "Value", "iec60870_asdu.scalval", FT_INT16, BASE_DEC, NULL, 0x0,
"Scaled value", HFILL }},
{ &hf_asdu_raw_data,
- { "Raw Data", "104asdu.rawdata", FT_BYTES, BASE_NONE, NULL, 0x0,
+ { "Raw Data", "iec60870_asdu.rawdata", FT_BYTES, BASE_NONE, NULL, 0x0,
"Information object raw data", HFILL }},
};
@@ -1910,29 +2111,192 @@ proto_register_iec104asdu(void)
{ &ei_iec104_apdu_invalid_len, { "iec104.apdu_invalid_len", PI_MALFORMED, PI_ERROR, "Invalid ApduLen", EXPFILL }},
};
- expert_module_t* expert_iec104;
+ expert_module_t* expert_iec60870;
+
+ proto_iec60870_asdu = proto_register_protocol("IEC 60870-5-101/104 ASDU", "IEC 60870-5-101/104 ASDU", "iec60870_asdu");
+ iec60870_asdu_handle = create_dissector_handle(dissect_iec60870_asdu, proto_iec60870_asdu);
- proto_iec104asdu = proto_register_protocol("IEC 60870-5-104-Asdu", "104asdu", "104asdu");
+ /* Provide an alias to the previous name of this dissector */
+ proto_register_alias(proto_iec60870_asdu, "104asdu");
- proto_register_field_array(proto_iec104asdu, hf_as, array_length(hf_as));
+ proto_register_field_array(proto_iec60870_asdu, hf_as, array_length(hf_as));
proto_register_subtree_array(ett_as, array_length(ett_as));
- expert_iec104 = expert_register_protocol(proto_iec104asdu);
- expert_register_field_array(expert_iec104, ei, array_length(ei));
+ expert_iec60870 = expert_register_protocol(proto_iec60870_asdu);
+ expert_register_field_array(expert_iec60870, ei, array_length(ei));
+
+}
+
+/* The registration hand-off routine */
+void
+proto_reg_handoff_iec60870_104(void)
+{
+ dissector_handle_t iec60870_104_handle;
+
+ iec60870_104_handle = create_dissector_handle(dissect_iec60870_104_tcp, proto_iec60870_104);
+
+ dissector_add_uint_with_preference("tcp.port", IEC104_PORT, iec60870_104_handle);
+ apply_iec60870_104_prefs();
+}
+
+static void
+apply_iec60870_101_prefs(void)
+{
+ /* IEC101 uses user customizable preferences for the configurable field lengths */
+ global_iec60870_cot_len = prefs_get_uint_value("iec60870_101", "cot_len");
+ global_iec60870_asdu_addr_len = prefs_get_uint_value("iec60870_101", "asdu_addr_len");
+ global_iec60870_ioa_len = prefs_get_uint_value("iec60870_101", "asdu_ioa_len");
+}
+
+
+/******************************************************************************************************/
+/* Return length of IEC 101 Protocol over TCP message (used for re-assembly) */
+/******************************************************************************************************/
+static guint
+get_iec101_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset _U_, void *data _U_)
+{
+
+ guint len=0, type;
+ type = tvb_get_guint8(tvb, offset);
+
+ switch (type) {
+ case IEC101_SINGLE_CHAR:
+ len = 1;
+ break;
+ case IEC101_FIXED_LEN:
+ len = 5;
+ break;
+ case IEC101_VAR_LEN:
+ len = tvb_get_guint8(tvb, offset+1) + 6;
+ break;
+ }
+
+ return len;
}
+/******************************************************************************************************/
+/* Dissect (and possibly Re-assemble) IEC 101 protocol payload data */
+/******************************************************************************************************/
+static int
+dissect_iec60870_101_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
+{
+
+ guint type = tvb_get_guint8(tvb, 0);
+
+ /* Check that this is actually a IEC 60870-5-101 packet. */
+ switch (type) {
+ case IEC101_SINGLE_CHAR:
+ case IEC101_FIXED_LEN:
+ case IEC101_VAR_LEN:
+ tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 1, get_iec101_len, dissect_iec60870_101, data);
+ break;
+ default:
+ return 0;
+ }
+ return tvb_captured_length(tvb);
+}
/* The registration hand-off routine */
void
-proto_reg_handoff_iec104(void)
+proto_register_iec60870_101(void)
+{
+ /* IEC 101 Protocol header fields */
+ static hf_register_info iec60870_101_hf[] = {
+ { &hf_iec60870_101_frame,
+ { "Frame Format", "iec60870_101.header", FT_UINT8, BASE_HEX, VALS(iec60870_101_frame_vals), 0x0, NULL, HFILL }},
+ { &hf_iec60870_101_length,
+ { "Length", "iec60870_101.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
+ { &hf_iec60870_101_num_user_octets,
+ { "Number of User Octets", "iec60870_101.num_user_octets", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
+ { &hf_iec60870_101_ctrlfield,
+ { "Control Field", "iec60870_101.ctrlfield", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
+ { &hf_iec60870_101_ctrl_prm,
+ { "PRM", "iec60870_101.ctrl_prm", FT_UINT8, BASE_DEC, VALS(iec60870_101_ctrl_prm_values), 0x40, "Primary Message", HFILL }},
+ { &hf_iec60870_101_ctrl_fcb,
+ { "FCB", "iec60870_101.ctrl_fcb", FT_UINT8, BASE_DEC, NULL, 0x20, "Frame Count Bit", HFILL }},
+ { &hf_iec60870_101_ctrl_fcv,
+ { "FCV", "iec60870_101.ctrl_fcv", FT_UINT8, BASE_DEC, NULL, 0x10, "Frame Count Bit Valid", HFILL }},
+ { &hf_iec60870_101_ctrl_dfc,
+ { "DFC", "iec60870_101.ctrl_dfc", FT_UINT8, BASE_DEC, NULL, 0x10, "Data Flow Control", HFILL }},
+ { &hf_iec60870_101_ctrl_func_pri_to_sec,
+ { "CF Func Code", "iec60870_101.ctrl_func_pri_to_sec", FT_UINT8, BASE_DEC, VALS(iec60870_101_ctrl_func_pri_to_sec_values), 0x0F, "Control Field Function Code, Pri to Sec", HFILL }},
+ { &hf_iec60870_101_ctrl_func_sec_to_pri,
+ { "CF Func Code", "iec60870_101.ctrl_func_sec_to_pri", FT_UINT8, BASE_DEC, VALS(iec60870_101_ctrl_func_sec_to_pri_values), 0x0F, "Control Field Function Code, Sec to Pri", HFILL }},
+ { &hf_iec60870_101_linkaddr,
+ { "Data Link Address", "iec60870_101.linkaddr", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
+ { &hf_iec60870_101_checksum,
+ { "Checksum", "iec60870_101.checksum", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
+ { &hf_iec60870_101_stopchar,
+ { "Stop Character", "iec60870_101.stopchar", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
+
+ };
+
+ /* Setup protocol subtree array */
+ static gint *ett_serial[] = {
+ &ett_iec60870_101,
+ &ett_iec60870_101_ctrlfield,
+ };
+
+ module_t *iec60870_101_module;
+
+ /* Register the protocol name and description */
+ proto_iec60870_101 = proto_register_protocol("IEC 60870-5-101", "IEC 60870-5-101", "iec60870_101");
+
+ /* Required function calls to register the header fields and subtrees used */
+ proto_register_field_array(proto_iec60870_101, iec60870_101_hf, array_length(iec60870_101_hf));
+ proto_register_subtree_array(ett_serial, array_length(ett_serial));
+
+ /* Register required preferences for IEC 101 configurable field lengths */
+ iec60870_101_module = prefs_register_protocol(proto_iec60870_101, NULL);
+
+ static const enum_val_t cot_len[] = {
+ {"1", "1 octet", 1},
+ {"2", "2 octet", 2},
+ {NULL, NULL, -1}
+ };
+
+ static const enum_val_t asdu_addr_len[] = {
+ {"1", "1 octet", 1},
+ {"2", "2 octet", 2},
+ {NULL, NULL, -1}
+ };
+
+ static const enum_val_t asdu_ioa_len[] = {
+ {"2", "2 octet", 2},
+ {"3", "3 octet", 3},
+ {NULL, NULL, -1}
+ };
+
+ prefs_register_enum_preference(iec60870_101_module, "cot_len",
+ "Length of the Cause of Transmission Field",
+ "Length of the Cause of Transmission Field, configurable in '101 and fixed at 2 octets with '104",
+ &global_iec60870_cot_len, cot_len, FALSE);
+
+ prefs_register_enum_preference(iec60870_101_module, "asdu_addr_len",
+ "Length of the Common ASDU Address Field",
+ "Length of the Common ASDU Address Field, configurable in '101 and fixed at 2 octets with '104",
+ &global_iec60870_asdu_addr_len, asdu_addr_len, FALSE);
+
+ prefs_register_enum_preference(iec60870_101_module, "asdu_ioa_len",
+ "Length of the Information Object Address Field",
+ "Length of the Information Object Address Field, configurable in '101 and fixed at 3 octets with '104",
+ &global_iec60870_ioa_len, asdu_ioa_len, FALSE);
+
+}
+
+void
+proto_reg_handoff_iec60870_101(void)
{
- dissector_handle_t iec104apci_handle;
+ dissector_handle_t iec60870_101_handle;
+
+ iec60870_101_handle = create_dissector_handle(dissect_iec60870_101_tcp, proto_iec60870_101);
- iec104apci_handle = create_dissector_handle(dissect_iec104reas, proto_iec104apci);
- iec104asdu_handle = create_dissector_handle(dissect_iec104asdu, proto_iec104asdu);
+ /* Add decode-as connection to determine user-customized TCP port */
+ dissector_add_for_decode_as_with_preference("tcp.port", iec60870_101_handle);
+ /* Add dissection for serial pcap files generated by the RTAC */
+ dissector_add_for_decode_as("rtacser.data", iec60870_101_handle);
- dissector_add_uint_with_preference("tcp.port", IEC104_PORT, iec104apci_handle);
- apply_iec104_prefs();
+ apply_iec60870_101_prefs();
}
/*