diff options
author | Michael Mann <mmann78@netscape.net> | 2013-07-16 14:01:45 +0000 |
---|---|---|
committer | Michael Mann <mmann78@netscape.net> | 2013-07-16 14:01:45 +0000 |
commit | 18ea28c45e32659a1eee3d94d9e9d942138c03bc (patch) | |
tree | 7daca90e369238aed4402aac1ff7b5855ad68229 /epan/dissectors/packet-iec104.c | |
parent | 3baf637267ea5ac6173acfc0a4c1dc9dd6016761 (diff) |
Improve iec104 dissector. Bug 8849 (https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8849)
1) Decoding type 70 - End of initialization
2) Decoding qualifier of interrogation for type 100
3) Table of data length for all object types => checking length on one place
4) Setting correct number of selected bytes for IOA
From Jiří Engelthaler
svn path=/trunk/; revision=50664
Diffstat (limited to 'epan/dissectors/packet-iec104.c')
-rw-r--r-- | epan/dissectors/packet-iec104.c | 933 |
1 files changed, 544 insertions, 389 deletions
diff --git a/epan/dissectors/packet-iec104.c b/epan/dissectors/packet-iec104.c index b9f9d8496a..a5c7ad61d9 100644 --- a/epan/dissectors/packet-iec104.c +++ b/epan/dissectors/packet-iec104.c @@ -56,9 +56,10 @@ struct asduheader { guint32 IOA; guint8 NumIx; guint8 SQ; + guint8 DataLength; }; -/* asdu command value/status structure */ +/* ASDU command value/status structure */ typedef struct { gboolean OFF; gboolean ON; @@ -73,8 +74,6 @@ typedef struct { gboolean LongP; /* Long Pulse */ gboolean Persist; /* Persistent output */ gboolean SE; /* Select (1) / Execute (0) */ - - } td_CmdInfo; #define IEC104_PORT 2404 @@ -102,19 +101,17 @@ static int proto_iec104asdu = -1; /* Type I is only lowest bit set to 0 */ #define I_TYPE 0 -#define I_TYPE2 2 #define S_TYPE 1 #define U_TYPE 3 #define APCI_TYPE_UNKNOWN 4 + static const value_string apci_types [] = { { I_TYPE, "I" }, { S_TYPE, "S" }, - { I_TYPE2, "I" }, { U_TYPE, "U" }, { 0, NULL } }; - /* Constants relative to the filed, independent of the field position in the byte */ /* U (Unnombered) constants */ #define U_STARTDT_ACT 0x01 @@ -133,7 +130,6 @@ static const value_string u_types[] = { { 0, NULL } }; - /* ASDU types (TypeId) */ #define M_SP_NA_1 1 /* single-point information */ #define M_DP_NA_1 3 /* double-point information */ @@ -246,6 +242,7 @@ static const value_string asdu_types [] = { { F_SC_NB_1, "F_SC_NB_1" }, { 0, NULL } }; + static const value_string asdu_lngtypes [] = { { M_SP_NA_1, "single-point information" }, { M_DP_NA_1, "double-point information" }, @@ -304,6 +301,68 @@ static const value_string asdu_lngtypes [] = { { 0, NULL } }; +typedef struct { + guint8 value; + guint8 length; +} td_asdu_length; + +static const td_asdu_length asdu_length [] = { + { M_SP_NA_1, 1 }, + { M_DP_NA_1, 1 }, + { M_ST_NA_1, 2 }, + { M_BO_NA_1, 5 }, + { M_ME_NA_1, 3 }, + { M_ME_NB_1, 3 }, + { M_ME_NC_1, 5 }, + { M_IT_NA_1, 5 }, + { M_PS_NA_1, 5 }, + { M_ME_ND_1, 2 }, + { M_SP_TB_1, 8 }, + { M_DP_TB_1, 8 }, + { M_ST_TB_1, 9 }, + { M_BO_TB_1, 12 }, + { M_ME_TD_1, 10 }, + { M_ME_TE_1, 10 }, + { M_ME_TF_1, 12 }, + { M_IT_TB_1, 12 }, + { M_EP_TD_1, 10 }, + { M_EP_TE_1, 11 }, + { M_EP_TF_1, 11 }, + { C_SC_NA_1, 1 }, + { C_DC_NA_1, 1 }, + { C_RC_NA_1, 1 }, + { C_SE_NA_1, 3 }, + { C_SE_NB_1, 3 }, + { C_SE_NC_1, 5 }, + { C_BO_NA_1, 4 }, + { C_SC_TA_1, 8 }, + { C_DC_TA_1, 8 }, + { C_RC_TA_1, 8 }, + { C_SE_TA_1, 10 }, + { C_SE_TB_1, 10 }, + { C_SE_TC_1, 12 }, + { C_BO_TA_1, 11 }, + { M_EI_NA_1, 1 }, + { C_IC_NA_1, 1 }, + { C_CI_NA_1, 1 }, + { C_RD_NA_1, 0 }, + { C_CS_NA_1, 7 }, + { C_RP_NA_1, 1 }, + { C_TS_TA_1, 9 }, + { P_ME_NA_1, 3 }, + { P_ME_NB_1, 3 }, + { P_ME_NC_1, 5 }, + { P_AC_NA_1, 1 }, + { F_FR_NA_1, 6 }, + { F_SR_NA_1, 7 }, + { F_SC_NA_1, 4 }, + { F_LS_NA_1, 5 }, + { F_AF_NA_1, 4 }, + { F_SG_NA_1, 0 }, + { F_DR_TA_1, 13 }, + { F_SC_NB_1, 16 }, + { 0, 0 } +}; /* Cause of Transmision (CauseTx) */ #define Per_Cyc 1 @@ -345,6 +404,7 @@ static const value_string asdu_lngtypes [] = { #define UkCauseTx 45 #define UkComAdrASDU 46 #define UkIOA 47 + static const value_string causetx_types [] = { { Per_Cyc ,"Per/Cyc" }, { Back ,"Back" }, @@ -389,10 +449,10 @@ static const value_string causetx_types [] = { }; static const value_string diq_types[] = { - { 0, "IPOS0" }, + { 0, "Indeterminate or Intermediate" }, { 1, "OFF" }, { 2, "ON" }, - { 3, "IPOS3" }, + { 3, "Indeterminate" }, { 0, NULL } }; @@ -420,18 +480,49 @@ static const value_string rco_up_types[] = { { 0, NULL } }; +static const value_string coi_r_types[] = { + { 0, "Local power switch on" }, + { 1, "Local manual reset" }, + { 2, "Remote reset" }, + { 0, NULL } +}; +static const value_string qoi_r_types[] = { + { 0, "Not specified" }, + { 20, "Station interrogation (global)" }, + { 21, "Group 1 interrotagion" }, + { 22, "Group 2 interrotagion" }, + { 23, "Group 3 interrotagion" }, + { 24, "Group 4 interrotagion" }, + { 25, "Group 5 interrotagion" }, + { 26, "Group 6 interrotagion" }, + { 27, "Group 7 interrotagion" }, + { 28, "Group 8 interrotagion" }, + { 29, "Group 9 interrotagion" }, + { 30, "Group 10 interrotagion" }, + { 31, "Group 11 interrotagion" }, + { 32, "Group 12 interrotagion" }, + { 33, "Group 13 interrotagion" }, + { 34, "Group 14 interrotagion" }, + { 35, "Group 15 interrotagion" }, + { 36, "Group 16 interrotagion" }, + { 0, NULL } +}; static const true_false_string tfs_blocked_not_blocked = { "Blocked", "Not blocked" }; static const true_false_string tfs_substituted_not_substituted = { "Substituted", "Not Substituted" }; static const true_false_string tfs_not_topical_topical = { "Not Topical", "Topical" }; static const true_false_string tfs_invalid_valid = { "Invalid", "Valid" }; +static const true_false_string tfs_transient_not_transient = { "Transient", "Not Transient" }; static const true_false_string tfs_overflow_no_overflow = { "Overflow", "No overflow" }; static const true_false_string tfs_select_execute = { "Select", "Execute" }; +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" }; /* Protocol fields to be filtered */ static int hf_apdulen = -1; -static int hf_apcitype = -1; +static int hf_apcitype1 = -1; +static int hf_apcitype2 = -1; static int hf_apciutype = -1; static int hf_apcitx = -1; static int hf_apcirx = -1; @@ -447,14 +538,23 @@ static int hf_ioa = -1; static int hf_numix = -1; static int hf_sq = -1; static int hf_cp56time = -1; +static int hf_cp56time_ms = -1; +static int hf_cp56time_min = -1; +static int hf_cp56time_iv = -1; +static int hf_cp56time_hour = -1; +static int hf_cp56time_su = -1; +static int hf_cp56time_day = -1; +static int hf_cp56time_dow = -1; +static int hf_cp56time_month = -1; +static int hf_cp56time_year = -1; static int hf_siq = -1; -static int hf_siq_on = -1; +static int hf_siq_spi = -1; static int hf_siq_bl = -1; static int hf_siq_sb = -1; static int hf_siq_nt = -1; static int hf_siq_iv = -1; static int hf_diq = -1; -static int hf_diq_value = -1; +static int hf_diq_dpi = -1; static int hf_diq_bl = -1; static int hf_diq_sb = -1; static int hf_diq_nt = -1; @@ -466,7 +566,9 @@ static int hf_qds_sb = -1; static int hf_qds_nt = -1; static int hf_qds_iv = -1; static int hf_vti = -1; -static int hf_vti_tr = -1; +static int hf_vti_v = -1; +static int hf_vti_t = -1; +static int hf_qos = -1; static int hf_qos_ql = -1; static int hf_qos_se = -1; static int hf_sco = -1; @@ -481,20 +583,28 @@ static int hf_rco = -1; static int hf_rco_up = -1; static int hf_rco_qu = -1; static int hf_rco_se = -1; +static int hf_coi = -1; +static int hf_coi_r = -1; +static int hf_coi_i = -1; +static int hf_qoi = -1; static gint hf_asdu_bitstring = -1; static gint hf_asdu_float = -1; static gint hf_asdu_normval = -1; +static gint hf_asdu_scalval = -1; static gint ett_apci = -1; static gint ett_asdu = -1; static gint ett_asdu_objects = -1; static gint ett_siq = -1; static gint ett_diq = -1; +static gint ett_vti = -1; static gint ett_qds = -1; +static gint ett_qos = -1; static gint ett_sco = -1; static gint ett_dco = -1; static gint ett_rco = -1; +static gint ett_cp56time = -1; static expert_field ei_iec104_short_asdu = EI_INIT; static expert_field ei_iec104_apdu_min_len = EI_INIT; @@ -502,55 +612,90 @@ static expert_field ei_iec104_apdu_min_len = EI_INIT; /* Misc. functions for dissection of signal values */ /* ==================================================================== - void get_CP56Time( td_CP56Time *cp56t, tvbuff_t *tvb, guint8 offset) + void get_CP56Time(td_CP56Time *cp56t, tvbuff_t *tvb, guint8 offset) Dissects the CP56Time2a time (Seven octet binary time) that starts 'offset' bytes in 'tvb'. - The time and date is put in struct 'cp56t' ==================================================================== */ -static void get_CP56Time(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree ) +static void get_CP56Time(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) { guint16 ms; - guint8 valid; + guint8 value; + guint8 su; struct tm tm; nstime_t datetime; proto_item* ti; + proto_tree* cp56time_tree; - ms = tvb_get_letohs( tvb , *offset ); - (*offset) += 2; + ms = tvb_get_letohs(tvb, *offset); tm.tm_sec = ms / 1000; - datetime.nsecs = ms * 1000000; - - tm.tm_min = tvb_get_guint8(tvb, *offset); - /* "Invalid" -- Todo: test */ - valid = tm.tm_min & 0x80; + datetime.nsecs = (ms % 1000) * 1000000; + (*offset) += 2; - tm.tm_min &= 0x3F; + value = tvb_get_guint8(tvb, *offset); + tm.tm_min = value & 0x3F; (*offset)++; - tm.tm_hour = 0x1F & tvb_get_guint8(tvb, *offset); + + value = tvb_get_guint8(tvb, *offset); + tm.tm_hour = value & 0x1F; + su = value & 0x80; (*offset)++; - tm.tm_mday = tvb_get_guint8(tvb, *offset) & 0x1F; + + value = tvb_get_guint8(tvb, *offset); + tm.tm_mday = value & 0x1F; (*offset)++; - tm.tm_mon = 0x0F & tvb_get_guint8(tvb, *offset); + + value = tvb_get_guint8(tvb, *offset); + tm.tm_mon = (value & 0x0F) - 1; (*offset)++; - tm.tm_year = 0x7F & tvb_get_guint8(tvb, *offset); + + value = tvb_get_guint8(tvb, *offset); + tm.tm_year = value & 0x7F; + if (tm.tm_year < 70) + tm.tm_year += 100; + (*offset)++; - tm.tm_isdst = -1; /* there's no info on whether DST was in force; assume it's - * the same as currently */ + if (su) + tm.tm_isdst = 1; + else + tm.tm_isdst = -1; /* there's no info on whether DST was in force; assume it's + * the same as currently */ datetime.secs = mktime(&tm); - ti = proto_tree_add_time(iec104_header_tree, hf_cp56time, tvb, (*offset)-7, 7, &datetime); - proto_item_append_text(ti, "%s", valid ? "Invalid":"Valid"); -} + (*offset) -= 7; + + ti = proto_tree_add_time(iec104_header_tree, hf_cp56time, tvb, *offset, 7, &datetime); + cp56time_tree = proto_item_add_subtree(ti, ett_cp56time); + + proto_tree_add_item(cp56time_tree, hf_cp56time_ms, tvb, *offset, 2, ENC_LITTLE_ENDIAN); + (*offset) += 2; + + proto_tree_add_item(cp56time_tree, hf_cp56time_min, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + proto_tree_add_item(cp56time_tree, hf_cp56time_iv, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + (*offset) ++; + + proto_tree_add_item(cp56time_tree, hf_cp56time_hour, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + proto_tree_add_item(cp56time_tree, hf_cp56time_su, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + (*offset) ++; + + proto_tree_add_item(cp56time_tree, hf_cp56time_day, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + proto_tree_add_item(cp56time_tree, hf_cp56time_dow, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + (*offset) ++; + + proto_tree_add_item(cp56time_tree, hf_cp56time_month, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + (*offset) ++; + proto_tree_add_item(cp56time_tree, hf_cp56time_year, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + (*offset) ++; +} /* ==================================================================== 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) { proto_item* ti; @@ -562,21 +707,40 @@ static proto_item* get_InfoObjectAddress( guint32 *asdu_info_obj_addr, tvbuff_t return ti; } - - +/* ==================================================================== + TypeId length + ==================================================================== */ +static guint8 get_TypeIdLength(guint8 TypeId) +{ + guint8 ret = 0; + td_asdu_length *item; + + item = (td_asdu_length *)asdu_length; + while (item->value) + { + if (item->value == TypeId) + { + ret = item->length; + break; + } + item++; + } + + return ret; +} /* ==================================================================== SIQ: Single-point information (IEV 371-02-07) w quality descriptor ==================================================================== */ -static void get_SIQ( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree ) +static void get_SIQ(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) { proto_item* ti; proto_tree* siq_tree; ti = proto_tree_add_item(iec104_header_tree, hf_siq, tvb, *offset, 1, ENC_LITTLE_ENDIAN); - siq_tree = proto_item_add_subtree( ti, ett_siq ); + siq_tree = proto_item_add_subtree(ti, ett_siq); - proto_tree_add_item(siq_tree, hf_siq_on, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + proto_tree_add_item(siq_tree, hf_siq_spi, tvb, *offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(siq_tree, hf_siq_bl, tvb, *offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(siq_tree, hf_siq_sb, tvb, *offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(siq_tree, hf_siq_nt, tvb, *offset, 1, ENC_LITTLE_ENDIAN); @@ -588,34 +752,33 @@ static void get_SIQ( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tr /* ==================================================================== DIQ: Double-point information (IEV 371-02-08) w quality descriptor ==================================================================== */ -static void get_DIQ( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree ) +static void get_DIQ(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) { proto_item* ti; proto_tree* diq_tree; ti = proto_tree_add_item(iec104_header_tree, hf_diq, tvb, *offset, 1, ENC_LITTLE_ENDIAN); - diq_tree = proto_item_add_subtree( ti, ett_diq ); + diq_tree = proto_item_add_subtree(ti, ett_diq); - proto_tree_add_item(diq_tree, hf_diq_value, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + proto_tree_add_item(diq_tree, hf_diq_dpi, tvb, *offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(diq_tree, hf_diq_bl, tvb, *offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(diq_tree, hf_diq_sb, tvb, *offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(diq_tree, hf_diq_nt, tvb, *offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(diq_tree, hf_diq_iv, tvb, *offset, 1, ENC_LITTLE_ENDIAN); (*offset)++; - } /* ==================================================================== QDS: Quality descriptor (separate octet) ==================================================================== */ -static void get_QDS( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree ) +static void get_QDS(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) { proto_item* ti; proto_tree* qds_tree; ti = proto_tree_add_item(iec104_header_tree, hf_qds, tvb, *offset, 1, ENC_LITTLE_ENDIAN); - qds_tree = proto_item_add_subtree( ti, ett_qds ); + qds_tree = proto_item_add_subtree(ti, ett_qds); proto_tree_add_item(qds_tree, hf_qds_ov, tvb, *offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(qds_tree, hf_qds_bl, tvb, *offset, 1, ENC_LITTLE_ENDIAN); @@ -631,7 +794,7 @@ static void get_QDS( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tr (separate octet) ==================================================================== */ #if 0 -static void get_QDP( tvbuff_t *tvb _U_, guint8 *offset _U_, proto_tree *iec104_header_tree _U_ ) +static void get_QDP(tvbuff_t *tvb _U_, guint8 *offset _U_, proto_tree *iec104_header_tree _U_) { /* todo */ @@ -641,10 +804,16 @@ static void get_QDP( tvbuff_t *tvb _U_, guint8 *offset _U_, proto_tree *iec104_h /* ==================================================================== VTI: Value with transient state indication ==================================================================== */ -static void get_VTI( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) +static void get_VTI(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) { - proto_tree_add_item(iec104_header_tree, hf_vti, tvb, *offset, 1, ENC_LITTLE_ENDIAN); - proto_tree_add_item(iec104_header_tree, hf_vti_tr, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + proto_item* ti; + proto_tree* vti_tree; + + ti = proto_tree_add_item(iec104_header_tree, hf_vti, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + vti_tree = proto_item_add_subtree(ti, ett_vti); + + proto_tree_add_item(vti_tree, hf_vti_v, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + proto_tree_add_item(vti_tree, hf_vti_t, tvb, *offset, 1, ENC_LITTLE_ENDIAN); (*offset)++; } @@ -652,7 +821,7 @@ static void get_VTI( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tr /* ==================================================================== NVA: Normalized value ==================================================================== */ -static void get_NVA( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree ) +static void get_NVA(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) { /* Normalized value F16[1..16]<-1..+1-2^-15> */ proto_tree_add_item(iec104_header_tree, hf_asdu_normval, tvb, *offset, 2, ENC_LITTLE_ENDIAN); @@ -662,7 +831,7 @@ static void get_NVA( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tr (*offset) += 2; } -static void get_NVAspt( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree ) +static void get_NVAspt(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) { /* Normalized value F16[1..16]<-1..+1-2^-15> */ proto_tree_add_item(iec104_header_tree, hf_asdu_normval, tvb, *offset, 2, ENC_LITTLE_ENDIAN); @@ -675,18 +844,18 @@ static void get_NVAspt( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header /* ==================================================================== SVA: Scaled value ==================================================================== */ -static void get_SVA( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree ) +static void get_SVA(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) { /* Scaled value I16[1..16]<-2^15..+2^15-1> */ - proto_tree_add_item(iec104_header_tree, hf_asdu_normval, tvb, *offset, 2, ENC_LITTLE_ENDIAN); + proto_tree_add_item(iec104_header_tree, hf_asdu_scalval, tvb, *offset, 2, ENC_LITTLE_ENDIAN); (*offset) += 2; } -static void get_SVAspt( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree ) +static void get_SVAspt(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) { /* Scaled value I16[1..16]<-2^15..+2^15-1> */ - proto_tree_add_item(iec104_header_tree, hf_asdu_normval, tvb, *offset, 2, ENC_LITTLE_ENDIAN); + proto_tree_add_item(iec104_header_tree, hf_asdu_scalval, tvb, *offset, 2, ENC_LITTLE_ENDIAN); (*offset) += 2; } @@ -694,7 +863,7 @@ static void get_SVAspt( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header /* ==================================================================== "FLT": Short floating point number ==================================================================== */ -static void get_FLT( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree ) +static void get_FLT(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) { /* -------- IEEE 754 float value */ proto_tree_add_item(iec104_header_tree, hf_asdu_float, tvb, *offset, 4, ENC_LITTLE_ENDIAN); @@ -702,7 +871,7 @@ static void get_FLT( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tr (*offset) += 4; } -static void get_FLTspt( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree ) +static void get_FLTspt(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) { /* -------- IEEE 754 float value */ proto_tree_add_item(iec104_header_tree, hf_asdu_float, tvb, *offset, 4, ENC_LITTLE_ENDIAN); @@ -713,14 +882,14 @@ static void get_FLTspt( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header /* ==================================================================== "BSI": Binary state information, 32 bit ==================================================================== */ -static void get_BSI( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree ) +static void get_BSI(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) { proto_tree_add_bits_item(iec104_header_tree, hf_asdu_bitstring, tvb, *offset*8, 32, ENC_BIG_ENDIAN); (*offset) += 4; } -static void get_BSIspt( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree ) +static void get_BSIspt(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) { proto_tree_add_bits_item(iec104_header_tree, hf_asdu_bitstring, tvb, *offset*8, 32, ENC_BIG_ENDIAN); @@ -730,14 +899,13 @@ static void get_BSIspt( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header /* ==================================================================== todo -- BCR: Binary counter reading ==================================================================== */ -/* void get_BCR( guint8 *offset, - proto_tree *iec104_header_tree ); */ +/* void get_BCR(guint8 *offset, proto_tree *iec104_header_tree); */ /* ==================================================================== todo -- SEP: Single event of protection equipment ==================================================================== */ #if 0 -static void get_SEP( tvbuff_t *tvb _U_, guint8 *offset _U_, proto_tree *iec104_header_tree _U_ ) +static void get_SEP(tvbuff_t *tvb _U_, guint8 *offset _U_, proto_tree *iec104_header_tree _U_) { /* todo */ @@ -747,10 +915,16 @@ static void get_SEP( tvbuff_t *tvb _U_, guint8 *offset _U_, proto_tree *iec104_h /* ==================================================================== QOS: Qualifier Of Set-point command ==================================================================== */ -static void get_QOS( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree ) +static void get_QOS(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) { - proto_tree_add_item(iec104_header_tree, hf_qos_ql, tvb, *offset, 1, ENC_LITTLE_ENDIAN); - proto_tree_add_item(iec104_header_tree, hf_qos_se, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + proto_item* ti; + proto_tree* qos_tree; + + ti = proto_tree_add_item(iec104_header_tree, hf_qos, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + qos_tree = proto_item_add_subtree(ti, ett_qos); + + proto_tree_add_item(qos_tree, hf_qos_ql, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + proto_tree_add_item(qos_tree, hf_qos_se, tvb, *offset, 1, ENC_LITTLE_ENDIAN); (*offset)++; } @@ -758,13 +932,13 @@ static void get_QOS( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tr /* ==================================================================== SCO: Single Command (IEV 371-03-02) ==================================================================== */ -static void get_SCO( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree ) +static void get_SCO(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) { proto_item* ti; proto_tree* sco_tree; ti = proto_tree_add_item(iec104_header_tree, hf_sco, tvb, *offset, 1, ENC_LITTLE_ENDIAN); - sco_tree = proto_item_add_subtree( ti, ett_sco ); + sco_tree = proto_item_add_subtree(ti, ett_sco); proto_tree_add_item(sco_tree, hf_sco_on, tvb, *offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(sco_tree, hf_sco_qu, tvb, *offset, 1, ENC_LITTLE_ENDIAN); @@ -776,13 +950,13 @@ static void get_SCO( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tr /* ==================================================================== DCO: Double Command (IEV 371-03-03) ==================================================================== */ -static void get_DCO( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree ) +static void get_DCO(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) { proto_item* ti; proto_tree* dco_tree; ti = proto_tree_add_item(iec104_header_tree, hf_dco, tvb, *offset, 1, ENC_LITTLE_ENDIAN); - dco_tree = proto_item_add_subtree( ti, ett_dco ); + dco_tree = proto_item_add_subtree(ti, ett_dco); proto_tree_add_item(dco_tree, hf_dco_on, tvb, *offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(dco_tree, hf_dco_qu, tvb, *offset, 1, ENC_LITTLE_ENDIAN); @@ -794,13 +968,13 @@ static void get_DCO( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tr /* ==================================================================== RCO: Regulating step command (IEV 371-03-13) ==================================================================== */ -static void get_RCO( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree ) +static void get_RCO(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) { proto_item* ti; proto_tree* rco_tree; ti = proto_tree_add_item(iec104_header_tree, hf_rco, tvb, *offset, 1, ENC_LITTLE_ENDIAN); - rco_tree = proto_item_add_subtree( ti, ett_rco ); + rco_tree = proto_item_add_subtree(ti, ett_rco); proto_tree_add_item(rco_tree, hf_rco_up, tvb, *offset, 1, ENC_LITTLE_ENDIAN); proto_tree_add_item(rco_tree, hf_rco_qu, tvb, *offset, 1, ENC_LITTLE_ENDIAN); @@ -808,6 +982,33 @@ static void get_RCO( tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tr (*offset)++; } + +/* ==================================================================== + COI: Qualifier of interrogation + ==================================================================== */ +static void get_COI(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) +{ + proto_item* ti; + proto_tree* coi_tree; + + ti = proto_tree_add_item(iec104_header_tree, hf_coi, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + coi_tree = proto_item_add_subtree(ti, ett_rco); + + proto_tree_add_item(coi_tree, hf_coi_r, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + proto_tree_add_item(coi_tree, hf_coi_i, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + + (*offset)++; +} + +/* ==================================================================== + QOI: Qualifier of interrogation + ==================================================================== */ +static void get_QOI(tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree) +{ + proto_tree_add_item(iec104_header_tree, hf_qoi, tvb, *offset, 1, ENC_LITTLE_ENDIAN); + + (*offset)++; +} /* .... end Misc. functions for dissection of signal values */ @@ -818,9 +1019,9 @@ static guint get_iec104apdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offse guint8 Val; guint32 Off; - for (Off = 0; Off <= tvb_reported_length(tvb) - 2; Off++) { + for (Off = 0; Off <= tvb_reported_length(tvb) - 2; Off++) { Val = tvb_get_guint8(tvb, offset + Off); - if (Val == APCI_START) { + if (Val == APCI_START) { return (guint)(Off + tvb_get_guint8(tvb, offset + Off + 1) + 2); } } @@ -837,9 +1038,9 @@ static void dissect_iec104asdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tr const char *cause_str; size_t Ind; struct asduheader asduh; - proto_item *it104, *ioa_item; + proto_item *it104; proto_tree *it104tree; - + emem_strbuf_t * res; guint8 offset = 0; /* byte offset, signal dissection */ guint8 offset_start_ioa = 0; /* position first ioa */ @@ -849,56 +1050,71 @@ static void dissect_iec104asdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tr proto_tree * trSignal; col_set_str(pinfo->cinfo, COL_PROTOCOL, "104asdu"); - col_clear(pinfo->cinfo, COL_INFO); it104 = proto_tree_add_item(tree, proto_iec104asdu, tvb, 0, -1, ENC_NA); it104tree = proto_item_add_subtree(it104, ett_asdu); + res = ep_strbuf_new_label(NULL); + + /* Type identification */ asduh.TypeId = tvb_get_guint8(tvb, 0); proto_tree_add_item(it104tree, hf_typeid, tvb, 0, 1, ENC_LITTLE_ENDIAN); + asduh.DataLength = get_TypeIdLength(asduh.TypeId); + + /* Variable structure qualifier */ Bytex = tvb_get_guint8(tvb, 1); - asduh.NumIx = Bytex & 0x7F; 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); - if (asduh.NumIx > 1) - proto_tree_add_item(it104tree, hf_sq, tvb, 1, 1, ENC_LITTLE_ENDIAN); + + /* 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); + + /* Originator address */ asduh.OA = tvb_get_guint8(tvb, 3); proto_tree_add_item(it104tree, hf_oa, tvb, 3, 1, ENC_LITTLE_ENDIAN); + + /* Common address of ASDU */ asduh.Addr = tvb_get_letohs(tvb, 4); proto_tree_add_item(it104tree, hf_addr, tvb, 4, 2, ENC_LITTLE_ENDIAN); + + /* Information object address */ asduh.IOA = tvb_get_letoh24(tvb, 6); - proto_tree_add_item(it104tree, hf_ioa, tvb, 6, 3, ENC_LITTLE_ENDIAN); cause_str = val_to_str(asduh.TNCause & F_CAUSE, causetx_types, " <CauseTx=%u>"); - col_append_fstr( pinfo->cinfo, COL_INFO, "%u %s %u %s %s", asduh.Addr, pinfo->srcport == IEC104_PORT ? "->" : "<-", - asduh.OA, val_to_str(asduh.TypeId, asdu_types, "<TypeId=%u>"), cause_str); + + ep_strbuf_append_printf(res, "ASDU=%u %s %s", asduh.Addr, val_to_str(asduh.TypeId, asdu_types, "<TypeId=%u>"), cause_str); + if (asduh.TNCause & F_NEGA) - col_append_str( pinfo->cinfo, COL_INFO, "_NEGA"); + ep_strbuf_append(res, "_NEGA"); if (asduh.TNCause & F_TEST) - col_append_str( pinfo->cinfo, COL_INFO, "_TEST"); + ep_strbuf_append(res, "_TEST"); - if (asduh.TNCause & (F_TEST | F_NEGA)) { + if ((asduh.TNCause & (F_TEST | F_NEGA)) == 0) { for (Ind=strlen(cause_str); Ind< 7; Ind++) - col_append_str( pinfo->cinfo, COL_INFO, " "); + ep_strbuf_append(res, " "); } - col_append_fstr( pinfo->cinfo, COL_INFO, " IOA=%d", asduh.IOA); - if (asduh.NumIx > 1) { + if (asduh.NumIx > 1) { + ep_strbuf_append_printf(res, " IOA[%d]=%d", asduh.NumIx, asduh.IOA); if (asduh.SQ == F_SQ) - col_append_fstr( pinfo->cinfo, COL_INFO, "-%d", asduh.IOA + asduh.NumIx - 1); + ep_strbuf_append_printf(res, "-%d", asduh.IOA + asduh.NumIx - 1); else - col_append_str( pinfo->cinfo, COL_INFO, ",..."); - col_append_fstr( pinfo->cinfo, COL_INFO, " (%u) ", asduh.NumIx); + ep_strbuf_append(res, ",..."); } else { - col_append_str( pinfo->cinfo, COL_INFO, " "); + ep_strbuf_append_printf(res, " IOA=%d", asduh.IOA); } + col_append_str(pinfo->cinfo, COL_INFO, res->str); col_set_fence(pinfo->cinfo, COL_INFO); + /* 'ASDU Details': ROOT ITEM */ + proto_item_append_text(it104, ": %s '%s'", res->str, Len >= ASDU_HEAD_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 */ @@ -933,316 +1149,179 @@ static void dissect_iec104asdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tr case C_SE_TB_1: case C_SE_TC_1: case C_BO_TA_1: + case M_EI_NA_1: + case C_IC_NA_1: case C_CS_NA_1: - /* create subtree for the signal values ... */ - itSignal = proto_tree_add_text(it104tree, tvb, offset, -1, "Object values"); - trSignal = proto_item_add_subtree( itSignal, ett_asdu_objects ); - /* -- object values */ for(i = 0; i < asduh.NumIx; i++) { + /* create subtree for the signal values ... */ + itSignal = proto_tree_add_text(it104tree, tvb, offset, asduh.DataLength + 3, "IOA:s"); + trSignal = proto_item_add_subtree(itSignal, ett_asdu_objects); + /* -------- First Information object address */ if (!i) { offset_start_ioa = offset; /* -------- Information object address */ - asdu_info_obj_addr = asduh.IOA; - ioa_item = proto_tree_add_uint(trSignal, hf_ioa, tvb, offset_start_ioa, 3, asdu_info_obj_addr); /* check length */ - if( Len < (guint)(offset+3) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); + if(Len < (guint)(offset + 3)) { + expert_add_info(pinfo, itSignal, &ei_iec104_short_asdu); return; } - offset += 3; /* step over IOA bytes */ + get_InfoObjectAddress(&asdu_info_obj_addr, tvb, &offset, trSignal); } else { /* -------- following Information object address depending on SQ */ if (asduh.SQ) /* <=> SQ=1, info obj addr = startaddr++ */ { asdu_info_obj_addr++; - ioa_item = proto_tree_add_uint(trSignal, hf_ioa, tvb, offset_start_ioa, 3, asdu_info_obj_addr); + proto_tree_add_uint(trSignal, hf_ioa, tvb, offset_start_ioa, 3, asdu_info_obj_addr); } else { /* SQ=0, info obj addr given */ /* -------- Information object address */ /* check length */ - if( Len < (guint)(offset+3) ) { + if(Len < (guint)(offset + 3)) { expert_add_info(pinfo, itSignal, &ei_iec104_short_asdu); return; } - ioa_item = get_InfoObjectAddress( &asdu_info_obj_addr, tvb, &offset, trSignal); - + get_InfoObjectAddress(&asdu_info_obj_addr, tvb, &offset, trSignal); } } + proto_item_set_text(itSignal, "IOA: %d", asdu_info_obj_addr); + + /* check length */ + if(Len < (guint)(offset + asduh.DataLength)) { + expert_add_info(pinfo, itSignal, &ei_iec104_short_asdu); + return; + } + switch (asduh.TypeId) { case M_SP_NA_1: /* 1 Single-point information */ - /* check length */ - if( Len < (guint)(offset+1) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_SIQ( tvb, &offset, trSignal ); + get_SIQ(tvb, &offset, trSignal); break; case M_DP_NA_1: /* 3 Double-point information */ - /* check length */ - if( Len < (guint)(offset+1) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_DIQ( tvb, &offset, trSignal ); + get_DIQ(tvb, &offset, trSignal); break; case M_ST_NA_1: /* 5 Step position information */ - /* check length */ - if( Len < (guint)(offset+2) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_VTI( tvb, &offset, trSignal ); - get_QDS( tvb, &offset, trSignal ); + get_VTI(tvb, &offset, trSignal); + get_QDS(tvb, &offset, trSignal); break; case M_BO_NA_1: /* 7 Bitstring of 32 bits */ - /* check length */ - if( Len < (guint)(offset+5) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_BSI( tvb, &offset, trSignal ); - get_QDS( tvb, &offset, trSignal ); + get_BSI(tvb, &offset, trSignal); + get_QDS(tvb, &offset, trSignal); break; case M_ME_NA_1: /* 9 Measured value, normalized value */ - /* check length */ - if( Len < (guint)(offset+3) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_NVA( tvb, &offset, trSignal ); - get_QDS( tvb, &offset, trSignal ); + get_NVA(tvb, &offset, trSignal); + get_QDS(tvb, &offset, trSignal); break; case M_ME_NB_1: /* 11 Measured value, scaled value */ - /* check length */ - if( Len < (guint)(offset+3) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_SVA( tvb, &offset, trSignal ); - get_QDS( tvb, &offset, trSignal ); + get_SVA(tvb, &offset, trSignal); + get_QDS(tvb, &offset, trSignal); break; case M_ME_NC_1: /* 13 Measured value, short floating point value */ - /* check length */ - if( Len < (guint)(offset+5) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_FLT( tvb, &offset, trSignal ); - get_QDS( tvb, &offset, trSignal ); + get_FLT(tvb, &offset, trSignal); + get_QDS(tvb, &offset, trSignal); break; case M_ME_ND_1: /* 21 Measured value, normalized value without quality descriptor */ - /* check length */ - if( Len < (guint)(offset+2) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_NVA( tvb, &offset, trSignal ); + get_NVA(tvb, &offset, trSignal); break; case M_SP_TB_1: /* 30 Single-point information with time tag CP56Time2a */ - /* check length */ - if( Len < (guint)(offset+8) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_SIQ( tvb, &offset, trSignal ); - get_CP56Time( tvb, &offset, trSignal ); + get_SIQ(tvb, &offset, trSignal); + get_CP56Time(tvb, &offset, trSignal); break; case M_DP_TB_1: /* 31 Double-point information with time tag CP56Time2a */ - /* check length */ - if( Len < (guint)(offset+8) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_DIQ( tvb, &offset, trSignal ); - get_CP56Time( tvb, &offset, trSignal ); + get_DIQ(tvb, &offset, trSignal); + get_CP56Time(tvb, &offset, trSignal); break; case M_ST_TB_1: /* 32 Step position information with time tag CP56Time2a */ - /* check length */ - if( Len < (guint)(offset+9) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_VTI( tvb, &offset, trSignal ); - get_QDS( tvb, &offset, trSignal ); - get_CP56Time( tvb, &offset, trSignal ); + get_VTI(tvb, &offset, trSignal); + get_QDS(tvb, &offset, trSignal); + get_CP56Time(tvb, &offset, trSignal); break; case M_BO_TB_1: /* 33 bitstring of 32 bit with time tag CP56Time2a */ - /* check length */ - if( Len < (guint)(offset+12) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_BSI( tvb, &offset, trSignal ); - get_QDS( tvb, &offset, trSignal ); - get_CP56Time( tvb, &offset, trSignal ); + get_BSI(tvb, &offset, trSignal); + get_QDS(tvb, &offset, trSignal); + get_CP56Time(tvb, &offset, trSignal); break; case M_ME_TD_1: /* 34 Measured value, normalized value with time tag CP56Time2a */ - /* check length */ - if( Len < (guint)(offset+10) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_NVA( tvb, &offset, trSignal ); - get_QDS( tvb, &offset, trSignal ); - get_CP56Time( tvb, &offset, trSignal ); + get_NVA(tvb, &offset, trSignal); + get_QDS(tvb, &offset, trSignal); + get_CP56Time(tvb, &offset, trSignal); break; case M_ME_TE_1: /* 35 Measured value, scaled value with time tag CP56Time2a */ - /* check length */ - if( Len < (guint)(offset+10) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_SVA( tvb, &offset, trSignal ); - get_QDS( tvb, &offset, trSignal ); - get_CP56Time( tvb, &offset, trSignal ); + get_SVA(tvb, &offset, trSignal); + get_QDS(tvb, &offset, trSignal); + get_CP56Time(tvb, &offset, trSignal); break; case M_ME_TF_1: /* 36 Measured value, short floating point value with time tag CP56Time2a */ - /* check length */ - if( Len < (guint)(offset+12) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_FLT( tvb, &offset, trSignal ); - get_QDS( tvb, &offset, trSignal ); - get_CP56Time( tvb, &offset, trSignal ); + get_FLT(tvb, &offset, trSignal); + get_QDS(tvb, &offset, trSignal); + get_CP56Time(tvb, &offset, trSignal); break; case C_SC_NA_1: /* 45 Single command */ - /* check length */ - if( Len < (guint)(offset+1) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_SCO( tvb, &offset, trSignal ); + get_SCO(tvb, &offset, trSignal); break; case C_DC_NA_1: /* 46 Double command */ - /* check length */ - if( Len < (guint)(offset+1) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_DCO( tvb, &offset, trSignal ); + get_DCO(tvb, &offset, trSignal); break; case C_RC_NA_1: /* 47 Regulating step command */ - /* check length */ - if( Len < (guint)(offset+1) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_RCO( tvb, &offset, trSignal ); + get_RCO(tvb, &offset, trSignal); break; - case C_SE_NA_1: /* 48 Set point command, normalized value */ - /* check length */ - if( Len < (guint)(offset+3) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_NVAspt( tvb, &offset, trSignal ); - get_QOS( tvb, &offset, trSignal ); + case C_SE_NA_1: /* 48 Set point command, normalized value */ + get_NVAspt(tvb, &offset, trSignal); + get_QOS(tvb, &offset, trSignal); break; case C_SE_NB_1: /* 49 Set point command, scaled value */ - /* check length */ - if( Len < (guint)(offset+3) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_SVAspt( tvb, &offset, trSignal ); - get_QOS( tvb, &offset, trSignal ); + get_SVAspt(tvb, &offset, trSignal); + get_QOS(tvb, &offset, trSignal); break; case C_SE_NC_1: /* 50 Set point command, short floating point value */ - /* check length */ - if( Len < (guint)(offset+5) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_FLTspt( tvb, &offset, trSignal ); - get_QOS( tvb, &offset, trSignal ); + get_FLTspt(tvb, &offset, trSignal); + get_QOS(tvb, &offset, trSignal); break; case C_BO_NA_1: /* 51 Bitstring of 32 bits */ - /* check length */ - if( Len < (guint)(offset+4) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_BSIspt( tvb, &offset, trSignal ); - break; + get_BSIspt(tvb, &offset, trSignal); + break; case C_SC_TA_1: /* 58 Single command with time tag CP56Time2a */ - /* check length */ - if( Len < (guint)(offset+8) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_SCO( tvb, &offset, trSignal ); - get_CP56Time( tvb, &offset, trSignal ); + get_SCO(tvb, &offset, trSignal); + get_CP56Time(tvb, &offset, trSignal); break; case C_DC_TA_1: /* 59 Double command with time tag CP56Time2a */ - /* check length */ - if( Len < (guint)(offset+8) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_DCO( tvb, &offset, trSignal ); - get_CP56Time( tvb, &offset, trSignal ); + get_DCO(tvb, &offset, trSignal); + get_CP56Time(tvb, &offset, trSignal); break; case C_RC_TA_1: /* 60 Regulating step command with time tag CP56Time2a */ - /* check length */ - if( Len < (guint)(offset+8) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_RCO( tvb, &offset, trSignal ); - get_CP56Time( tvb, &offset, trSignal ); + get_RCO(tvb, &offset, trSignal); + get_CP56Time(tvb, &offset, trSignal); break; case C_SE_TA_1: /* 61 Set point command, normalized value with time tag CP56Time2a */ - /* check length */ - if( Len < (guint)(offset+10) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_NVAspt( tvb, &offset, trSignal ); - get_QOS( tvb, &offset, trSignal ); - get_CP56Time( tvb, &offset, trSignal ); + get_NVAspt(tvb, &offset, trSignal); + get_QOS(tvb, &offset, trSignal); + get_CP56Time(tvb, &offset, trSignal); break; case C_SE_TB_1: /* 62 Set point command, scaled value with time tag CP56Time2a */ - /* check length */ - if( Len < (guint)(offset+10) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_SVAspt( tvb, &offset, trSignal ); - get_QOS( tvb, &offset, trSignal ); - get_CP56Time( tvb, &offset, trSignal ); + get_SVAspt(tvb, &offset, trSignal); + get_QOS(tvb, &offset, trSignal); + get_CP56Time(tvb, &offset, trSignal); break; case C_SE_TC_1: /* 63 Set point command, short floating point value with time tag CP56Time2a */ - /* check length */ - if( Len < (guint)(offset+12) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_FLTspt( tvb, &offset, trSignal ); - get_QOS( tvb, &offset, trSignal ); - get_CP56Time( tvb, &offset, trSignal ); + get_FLTspt(tvb, &offset, trSignal); + get_QOS(tvb, &offset, trSignal); + get_CP56Time(tvb, &offset, trSignal); break; case C_BO_TA_1: /* 64 Bitstring of 32 bits with time tag CP56Time2a */ - /* check length */ - if( Len < (guint)(offset+11) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_BSIspt( tvb, &offset, trSignal ); - get_CP56Time( tvb, &offset, trSignal ); + get_BSIspt(tvb, &offset, trSignal); + get_CP56Time(tvb, &offset, trSignal); + break; + case M_EI_NA_1: /* 70 end of initialization */ + get_COI(tvb, &offset, trSignal); + break; + case C_IC_NA_1: /* 100 interrogation command */ + get_QOI(tvb, &offset, trSignal); break; case C_CS_NA_1: /* 103 clock synchronization command */ - /* check length */ - if( Len < (guint)(offset+7) ) { - expert_add_info(pinfo, ioa_item, &ei_iec104_short_asdu); - return; - } - get_CP56Time( tvb, &offset, trSignal ); + get_CP56Time(tvb, &offset, trSignal); break; default: @@ -1251,6 +1330,7 @@ static void dissect_iec104asdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tr } /* end 'for(i = 0; i < dui.asdu_vsq_no_of_obj; i++)' */ break; default: + proto_tree_add_item(it104tree, hf_ioa, tvb, offset, 3, ENC_LITTLE_ENDIAN); break; } /* end 'switch (asdu_typeid)' */ @@ -1262,89 +1342,99 @@ static void dissect_iec104asdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tr static void dissect_iec104apci(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { guint TcpLen = tvb_reported_length(tvb); - guint8 Start = 0, len, type = 0, temp8; - guint8 temp16; + guint8 Start, len, type, temp8; + guint16 temp16; guint Off; proto_item *it104, *ti; proto_tree *it104tree; + emem_strbuf_t * res; col_set_str(pinfo->cinfo, COL_PROTOCOL, "104apci"); - col_clear(pinfo->cinfo, COL_INFO); it104 = proto_tree_add_item(tree, proto_iec104apci, tvb, 0, -1, ENC_NA); it104tree = proto_item_add_subtree(it104, ett_apci); - for (Off = 0; Off <= TcpLen - 2; Off++) { + res = ep_strbuf_new_label(NULL); + + for (Off = 0; Off <= TcpLen - 2; Off++) { Start = tvb_get_guint8(tvb, Off); - if (Start == APCI_START) { + if (Start == APCI_START) { if (Off > 0) { proto_tree_add_item(it104tree, hf_apcidata, tvb, 0, Off, ENC_NA); - col_append_fstr( pinfo->cinfo, COL_INFO, "<ERR prefix %u bytes> ", Off); + ep_strbuf_append_printf(res, "<ERR prefix %u bytes> ", Off); } proto_item_set_len(it104, Off + APCI_LEN); proto_tree_add_text(it104tree, tvb, Off, 1, "START"); ti = proto_tree_add_item(it104tree, hf_apdulen, tvb, Off + 1, 1, ENC_LITTLE_ENDIAN); - proto_tree_add_item(it104tree, hf_apcitype, tvb, Off + 2, 1, ENC_LITTLE_ENDIAN); - + len = tvb_get_guint8(tvb, Off + 1); - if (len < APDU_MIN_LEN) { + if (len < APDU_MIN_LEN) { expert_add_info_format_text(pinfo, ti, &ei_iec104_apdu_min_len, "APDU less than %d bytes", APDU_MIN_LEN); - col_append_fstr( pinfo->cinfo, COL_INFO, "<ERR ApduLen=%u bytes> ", len); + ep_strbuf_append_printf(res, "<ERR ApduLen=%u bytes> ", len); return; } temp8 = tvb_get_guint8(tvb, Off + 2); - type = temp8 & 0x03; + if ((temp8 & 0x01) == 0) + type = 0; + else + type = temp8 & 0x03; + + if (type == I_TYPE) + proto_tree_add_item(it104tree, hf_apcitype1, tvb, Off + 2, 1, ENC_LITTLE_ENDIAN); + else + proto_tree_add_item(it104tree, hf_apcitype2, tvb, Off + 2, 1, ENC_LITTLE_ENDIAN); if (len <= APDU_MAX_LEN) { - col_append_fstr( pinfo->cinfo, COL_INFO, "%s %s (", + ep_strbuf_append_printf(res, "%s %s (", (pinfo->srcport == IEC104_PORT ? "->" : "<-"), val_to_str_const(type, apci_types, "<ERR>")); } else { - col_append_fstr( pinfo->cinfo, COL_INFO, "<ERR ApduLen=%u bytes> ", len); + ep_strbuf_append_printf(res, "<ERR ApduLen=%u bytes> ", len); } - switch(type) { + switch(type) { case I_TYPE: - case I_TYPE2: temp16 = tvb_get_letohs(tvb, Off + 2) >> 1; - col_append_fstr( pinfo->cinfo, COL_INFO, "%2.2d,", temp16); + ep_strbuf_append_printf(res, "%d,", temp16); proto_tree_add_uint(it104tree, hf_apcitx, tvb, Off+2, 2, temp16); case S_TYPE: temp16 = tvb_get_letohs(tvb, Off + 4) >> 1; - col_append_fstr( pinfo->cinfo, COL_INFO, "%2.2d) ", temp16); + ep_strbuf_append_printf(res, "%d) ", temp16); proto_tree_add_uint(it104tree, hf_apcirx, tvb, Off+4, 2, temp16); break; case U_TYPE: - col_append_fstr( pinfo->cinfo, COL_INFO, "%s) ", val_to_str_const((temp8 >> 2) & 0x3F, u_types, "<ERR>")); + ep_strbuf_append_printf(res, "%s) ", val_to_str_const((temp8 >> 2) & 0x3F, u_types, "<ERR>")); proto_tree_add_item(it104tree, hf_apciutype, tvb, Off + 2, 1, ENC_LITTLE_ENDIAN); break; } + + col_clear(pinfo->cinfo, COL_INFO); + col_append_sep_str(pinfo->cinfo, COL_INFO, " | ", res->str); + col_set_fence(pinfo->cinfo, COL_INFO); + + proto_item_append_text(it104, ": %s", res->str); + + if (type == I_TYPE) + call_dissector(iec104asdu_handle, tvb_new_subset(tvb, Off + APCI_LEN, -1, len - APCI_DATA_LEN), pinfo, tree); + /* Don't search more the APCI_START */ break; } } - if (Start != APCI_START) { + if (Start != APCI_START) { /* Everything is bad (no APCI found) */ proto_tree_add_item(it104tree, hf_apcidata, tvb, 0, Off, ENC_NA); return; } - if ((type == I_TYPE) || (type == I_TYPE2)) { - call_dissector(iec104asdu_handle, tvb_new_subset(tvb, Off + APCI_LEN, -1, len - APCI_DATA_LEN), pinfo, tree); - } else { - col_set_fence(pinfo->cinfo, COL_INFO); - } } - - - static void dissect_iec104reas(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { /* 5th parameter = 6 = minimum bytes received to calculate the length. @@ -1354,19 +1444,21 @@ static void dissect_iec104reas(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tr get_iec104apdu_len, dissect_iec104apci); } - /* The protocol has two subprotocols: Register APCI */ void proto_register_iec104apci(void) { - static hf_register_info hf_ap[] = { { &hf_apdulen, { "ApduLen", "104apci.apdulen", FT_UINT8, BASE_DEC, NULL, 0x0, "APDU Len", HFILL }}, - { &hf_apcitype, + { &hf_apcitype1, + { "Type", "104apci.type", FT_UINT8, BASE_HEX, VALS(apci_types), 0x01, + "APCI type", HFILL }}, + + { &hf_apcitype2, { "Type", "104apci.type", FT_UINT8, BASE_HEX, VALS(apci_types), 0x03, "APCI type", HFILL }}, @@ -1398,7 +1490,6 @@ proto_register_iec104apci(void) ); proto_register_field_array(proto_iec104apci, hf_ap, array_length(hf_ap)); proto_register_subtree_array(ett_ap, array_length(ett_ap)); - } @@ -1406,7 +1497,6 @@ proto_register_iec104apci(void) void proto_register_iec104asdu(void) { - static hf_register_info hf_as[] = { { &hf_addr, @@ -1433,7 +1523,6 @@ proto_register_iec104asdu(void) { "Test", "104asdu.test", FT_BOOLEAN, 8, NULL, F_TEST, NULL, HFILL }}, - { &hf_ioa, { "IOA", "104asdu.ioa", FT_UINT24, BASE_DEC, NULL, 0x0, "Information Object Address", HFILL }}, @@ -1450,13 +1539,49 @@ proto_register_iec104asdu(void) { "CP56Time", "104asdu.cp56time", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0, NULL, HFILL }}, + { &hf_cp56time_ms, + { "MS", "104asdu.cp56time.ms", FT_UINT16, BASE_DEC, NULL, 0xFFFF, + "CP56Time milliseconds", HFILL }}, + + { &hf_cp56time_min, + { "Min", "104asdu.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, + "CP56Time invalid", HFILL }}, + + { &hf_cp56time_hour, + { "Hour", "104asdu.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, + "CP56Time summer time", HFILL }}, + + { &hf_cp56time_day, + { "Day", "104asdu.cp56time.day", FT_UINT8, BASE_DEC, NULL, 0x1F, + "CP56Time day", HFILL }}, + + { &hf_cp56time_dow, + { "DOW", "104asdu.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, + "CP56Time month", HFILL }}, + + { &hf_cp56time_year, + { "Year", "104asdu.cp56time.year", FT_UINT8, BASE_DEC, NULL, 0x7F, + "CP56Time year", HFILL }}, + { &hf_siq, { "SIQ", "104asdu.siq", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }}, - { &hf_siq_on, - { "SQ", "104asdu.siq.on", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x01, - "SIQ SQ", HFILL }}, + { &hf_siq_spi, + { "SPI", "104asdu.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, @@ -1478,9 +1603,9 @@ proto_register_iec104asdu(void) { "DIQ", "104asdu.diq", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }}, - { &hf_diq_value, - { "Value", "104asdu.diq.value", FT_UINT8, BASE_DEC, VALS(diq_types), 0x03, - "DIQ Value", HFILL }}, + { &hf_diq_dpi, + { "DPI", "104asdu.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, @@ -1523,81 +1648,108 @@ proto_register_iec104asdu(void) "QDS IV", HFILL }}, { &hf_vti, - { "VTI", "104asdu.vti", FT_UINT8, BASE_DEC, NULL, 0x7F, + { "VTI", "104asdu.vti", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }}, - { &hf_vti_tr, - { "VTI Transient", "104asdu.vti.ov", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x80, + { &hf_vti_v, + { "Value", "104asdu.vti.v", FT_UINT8, BASE_DEC, NULL, 0x7F, + "VTI Value", HFILL }}, + + { &hf_vti_t, + { "T", "104asdu.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, NULL, HFILL }}, { &hf_qos_ql, - { "QOS Qualifier", "104asdu.qos_ql", FT_UINT8, BASE_DEC, NULL, 0x7F, - NULL, HFILL }}, + { "QL", "104asdu.qos.ql", FT_UINT8, BASE_DEC, NULL, 0x7F, + "QOS QL", HFILL }}, { &hf_qos_se, - { "QOS S/E", "104asdu.qos_se", FT_BOOLEAN, 8, TFS(&tfs_select_execute), 0x80, - NULL, HFILL }}, + { "S/E", "104asdu.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, NULL, HFILL }}, { &hf_sco_on, - { "SCO ON/OFF", "104asdu.sco.on", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x01, - NULL, HFILL }}, + { "ON/OFF", "104asdu.sco.on", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x01, + "SCO SCS", HFILL }}, { &hf_sco_qu, - { "SCO QU", "104asdu.sco.qu", FT_UINT8, BASE_DEC, VALS(qos_qu_types), 0x7C, - NULL, HFILL }}, + { "QU", "104asdu.sco.qu", FT_UINT8, BASE_DEC, VALS(qos_qu_types), 0x7C, + "SCO QU", HFILL }}, { &hf_sco_se, - { "SCO S/E", "104asdu.sco.se", FT_BOOLEAN, 8, TFS(&tfs_select_execute), 0x80, - NULL, HFILL }}, + { "S/E", "104asdu.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, NULL, HFILL }}, { &hf_dco_on, - { "DCO ON/OFF", "104asdu.dco.on", FT_UINT8, BASE_DEC, VALS(dco_on_types), 0x03, - NULL, HFILL }}, + { "ON/OFF", "104asdu.dco.on", FT_UINT8, BASE_DEC, VALS(dco_on_types), 0x03, + "DCO DCS", HFILL }}, { &hf_dco_qu, - { "DCO QU", "104asdu.dco.qu", FT_UINT8, BASE_DEC, VALS(qos_qu_types), 0x7C, - NULL, HFILL }}, + { "QU", "104asdu.dco.qu", FT_UINT8, BASE_DEC, VALS(qos_qu_types), 0x7C, + "DCO QU", HFILL }}, { &hf_dco_se, - { "DCO S/E", "104asdu.dco.se", FT_BOOLEAN, 8, TFS(&tfs_select_execute), 0x80, - NULL, HFILL }}, + { "S/E", "104asdu.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, NULL, HFILL }}, { &hf_rco_up, - { "RCO UP/DOWN", "104asdu.rco.up", FT_UINT8, BASE_DEC, VALS(rco_up_types), 0x03, - NULL, HFILL }}, + { "UP/DOWN", "104asdu.rco.up", FT_UINT8, BASE_DEC, VALS(rco_up_types), 0x03, + "RCO RCS", HFILL }}, { &hf_rco_qu, - { "RCO QU", "104asdu.rco.qu", FT_UINT8, BASE_DEC, VALS(qos_qu_types), 0x7C, - NULL, HFILL }}, + { "QU", "104asdu.rco.qu", FT_UINT8, BASE_DEC, VALS(qos_qu_types), 0x7C, + "RCO QU", HFILL }}, { &hf_rco_se, - { "RCO S/E", "104asdu.rco.se", FT_BOOLEAN, 8, TFS(&tfs_select_execute), 0x80, + { "S/E", "104asdu.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, + NULL, HFILL }}, + + { &hf_coi_r, + { "R", "104asdu.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, + "COI I", HFILL }}, + + { &hf_qoi, + { "QOI", "104asdu.qoi", FT_UINT8, BASE_DEC, VALS(qoi_r_types), 0, NULL, HFILL }}, { &hf_asdu_bitstring, - { "Object value", "104asdu.bitstring", FT_UINT32, BASE_HEX, NULL, 0x0, - NULL, HFILL }}, + { "Value", "104asdu.bitstring", FT_UINT32, BASE_HEX, NULL, 0x0, + "BSI value", HFILL }}, { &hf_asdu_float, - { "Object value", "104asdu.float", FT_FLOAT, BASE_NONE, NULL, 0x0, - NULL, HFILL }}, + { "Value", "104asdu.float", FT_FLOAT, BASE_NONE, NULL, 0x0, + "Float value", HFILL }}, { &hf_asdu_normval, - { "Object value", "104asdu.normval", FT_INT16, BASE_DEC, NULL, 0x0, - NULL, HFILL }}, + { "Value", "104asdu.normval", FT_INT16, BASE_DEC, NULL, 0x0, + "Normalised value", HFILL }}, + { &hf_asdu_scalval, + { "Value", "104asdu.scalval", FT_INT16, BASE_DEC, NULL, 0x0, + "Scaled value", HFILL }}, }; static gint *ett_as[] = { @@ -1606,9 +1758,12 @@ proto_register_iec104asdu(void) &ett_siq, &ett_diq, &ett_qds, + &ett_qos, + &ett_vti, &ett_sco, &ett_dco, - &ett_rco + &ett_rco, + &ett_cp56time }; static ei_register_info ei[] = { @@ -1623,11 +1778,11 @@ proto_register_iec104asdu(void) "104asdu", "104asdu" ); + proto_register_field_array(proto_iec104asdu, 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)); - } |