3 * Author: Nikolas Koesling, 2023 (nikolas@koesling.info)
4 * Description: Wireshark dissector for the SINEC AP protocol according to
5 * https://cache.industry.siemens.com/dl/files/274/22090274/att_83836/v1/447_840_840C_880_Computer_Link_General_Description.pdf
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * SPDX-License-Identifier: GPL-2.0-or-later
15 #include <epan/packet.h>
17 #define PROTO_TAG_AP "SINEC-AP"
19 /* Min. telegram length for heuristic check */
20 #define TXP_MIN_TELEGRAM_LENGTH 22
22 /* Wireshark ID of the AP1 protocol */
25 static int hf_ap_protoid
;
26 static int hf_ap_mpxadr
;
27 static int hf_ap_comcls
;
28 static int hf_ap_comcod
;
29 static int hf_ap_modfr1
;
30 static int hf_ap_modfr2
;
31 static int hf_ap_errcls
;
32 static int hf_ap_errcod
;
33 static int hf_ap_rosctr
;
34 static int hf_ap_sgsqnr
;
35 static int hf_ap_tactid
;
36 static int hf_ap_tasqnr
;
37 static int hf_ap_spare
;
38 static int hf_ap_pduref
;
39 static int hf_ap_pduid
;
40 static int hf_ap_pdulg
;
41 static int hf_ap_parlg
;
42 static int hf_ap_datlg
;
46 static heur_dissector_list_t ap_heur_subdissector_list
;
48 static const value_string vs_comcls
[] = {
49 {0x0, "ACK without data"},
50 {0x4, "Serial transfer"},
54 static const value_string vs_protid
[] = {
55 {0x0, "SINEC AP 1.0"},
60 dissect_ap(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree _U_
, void *data _U_
)
62 /*----------------- Heuristic Checks - Begin */
63 /* 1) check for minimum length */
64 if (tvb_captured_length(tvb
) < TXP_MIN_TELEGRAM_LENGTH
)
67 /* 2) protocol id == 0 */
68 if (tvb_get_uint8(tvb
, 0) != 0)
70 /*----------------- Heuristic Checks - End */
72 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, PROTO_TAG_AP
);
73 col_clear(pinfo
->cinfo
, COL_INFO
);
75 uint8_t comcls
= tvb_get_uint8(tvb
, 2);
78 uint16_t pdulg
= tvb_get_uint16(tvb
, offset
, ENC_BIG_ENDIAN
);
81 uint16_t datlg
= tvb_get_uint16(tvb
, offset
, ENC_BIG_ENDIAN
);
84 ws_assert(offset
== 22);
86 /* check pdu and data length */
87 if (pdulg
!= tvb_captured_length(tvb
))
89 if (datlg
!= tvb_captured_length(tvb
) - 22)
95 col_append_str(pinfo
->cinfo
, COL_INFO
, "ACK without data");
100 col_append_str(pinfo
->cinfo
, COL_INFO
, "Serial transfer");
104 col_append_str(pinfo
->cinfo
, COL_INFO
, "UNKNOWN command class");
107 proto_item
*ap_item
= proto_tree_add_item(tree
, proto_ap
, tvb
, 0, -1, ENC_NA
);
108 proto_tree
*ap_tree
= proto_item_add_subtree(ap_item
, ett_ap
);
111 proto_tree_add_item(ap_tree
, hf_ap_protoid
, tvb
, offset
++, 1, ENC_BIG_ENDIAN
);
112 proto_tree_add_item(ap_tree
, hf_ap_mpxadr
, tvb
, offset
++, 1, ENC_BIG_ENDIAN
);
113 proto_tree_add_item(ap_tree
, hf_ap_comcls
, tvb
, offset
++, 1, ENC_BIG_ENDIAN
);
114 proto_tree_add_item(ap_tree
, hf_ap_comcod
, tvb
, offset
++, 1, ENC_BIG_ENDIAN
);
119 proto_tree_add_item(ap_tree
, hf_ap_errcls
, tvb
, offset
++, 1, ENC_BIG_ENDIAN
);
120 proto_tree_add_item(ap_tree
, hf_ap_errcod
, tvb
, offset
++, 1, ENC_BIG_ENDIAN
);
125 proto_tree_add_item(ap_tree
, hf_ap_modfr1
, tvb
, offset
++, 1, ENC_BIG_ENDIAN
);
126 proto_tree_add_item(ap_tree
, hf_ap_modfr2
, tvb
, offset
++, 1, ENC_BIG_ENDIAN
);
130 proto_tree_add_item(ap_tree
, hf_ap_modfr1
, tvb
, offset
++, 1, ENC_BIG_ENDIAN
);
131 proto_tree_add_item(ap_tree
, hf_ap_modfr2
, tvb
, offset
++, 1, ENC_BIG_ENDIAN
);
134 proto_tree_add_item(ap_tree
, hf_ap_rosctr
, tvb
, offset
++, 1, ENC_BIG_ENDIAN
);
135 proto_tree_add_item(ap_tree
, hf_ap_sgsqnr
, tvb
, offset
++, 1, ENC_BIG_ENDIAN
);
136 proto_tree_add_item(ap_tree
, hf_ap_tactid
, tvb
, offset
++, 1, ENC_BIG_ENDIAN
);
137 proto_tree_add_item(ap_tree
, hf_ap_tasqnr
, tvb
, offset
++, 1, ENC_BIG_ENDIAN
);
138 proto_tree_add_item(ap_tree
, hf_ap_spare
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
140 proto_tree_add_item(ap_tree
, hf_ap_pduref
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
142 proto_tree_add_item(ap_tree
, hf_ap_pduid
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
144 proto_tree_add_item(ap_tree
, hf_ap_pdulg
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
146 proto_tree_add_item(ap_tree
, hf_ap_parlg
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
148 proto_tree_add_item(ap_tree
, hf_ap_datlg
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
151 ws_assert(offset
== 22);
153 if (tvb_reported_length_remaining(tvb
, offset
) > 0) {
154 struct tvbuff
*next_tvb
= tvb_new_subset_remaining(tvb
, offset
);
155 heur_dtbl_entry_t
*hdtbl_entry
;
156 if (!dissector_try_heuristic(ap_heur_subdissector_list
, next_tvb
, pinfo
, tree
, &hdtbl_entry
, NULL
)) {
157 call_data_dissector(next_tvb
, pinfo
, tree
);
165 proto_register_ap(void)
167 static hf_register_info hf
[] = {
168 {&hf_ap_protoid
, {"PROTID", "sinecap.protid", FT_UINT8
, BASE_HEX
, VALS(vs_protid
), 0x0, "Protocol version", HFILL
}},
169 {&hf_ap_mpxadr
, {"MPXADR", "sinecap.mpxadr", FT_UINT8
, BASE_HEX
, NULL
, 0x0, "Multiplex address", HFILL
}},
170 {&hf_ap_comcls
, {"COMCLS", "sinecap.comcls", FT_UINT8
, BASE_HEX
, VALS(vs_comcls
), 0x0, "Command class", HFILL
}},
171 {&hf_ap_comcod
, {"COMCOD", "sinecap.comcod", FT_UINT8
, BASE_HEX
, NULL
, 0x0, "Command code", HFILL
}},
172 {&hf_ap_modfr1
, {"MODFR1", "sinecap.modfr1", FT_UINT8
, BASE_HEX
, NULL
, 0x0, "Modifier 1", HFILL
}},
173 {&hf_ap_errcls
, {"ERRCLS", "sinecap.errcls", FT_UINT8
, BASE_HEX
, NULL
, 0x0, "Error class", HFILL
}},
174 {&hf_ap_modfr2
, {"MODFR2", "sinecap.modfr2", FT_UINT8
, BASE_HEX
, NULL
, 0x0, "Modifier 2", HFILL
}},
175 {&hf_ap_errcod
, {"ERRCOD", "sinecap.errcod", FT_UINT8
, BASE_HEX
, NULL
, 0x0, "Error code", HFILL
}},
176 {&hf_ap_rosctr
, {"ROSCTR", "sinecap.rosctr", FT_UINT8
, BASE_HEX
, NULL
, 0x0, "Remote operating service", HFILL
}},
177 {&hf_ap_sgsqnr
, {"SGSQNR", "sinecap.sgsqnr", FT_UINT8
, BASE_HEX_DEC
, NULL
, 0x0, "Segment sequence number", HFILL
}},
178 {&hf_ap_tactid
, {"TACTID", "sinecap.tactid", FT_UINT8
, BASE_HEX
, NULL
, 0x0, "Transaction identifier", HFILL
}},
179 {&hf_ap_tasqnr
, {"TASQNR", "sinecap.tasqnr", FT_UINT8
, BASE_HEX
, NULL
, 0x0, "Transaction sequence number", HFILL
}},
180 {&hf_ap_spare
, {"SPARE", "sinecap.spare", FT_UINT16
, BASE_HEX
, NULL
, 0x0, "Free space", HFILL
}},
181 {&hf_ap_pduref
, {"PDUREF", "sinecap.pduref", FT_UINT16
, BASE_HEX
, NULL
, 0x0, "Protocol Data Unit reference", HFILL
}},
182 {&hf_ap_pduid
, {"PDUID", "sinecap.pduid", FT_UINT16
, BASE_HEX
, NULL
, 0x0, "Protocol Data Unit identifier", HFILL
}},
183 {&hf_ap_pdulg
, {"PDULG", "sinecap.pdulg", FT_UINT16
, BASE_HEX_DEC
, NULL
, 0x0, "Protocol Data Unit length", HFILL
}},
184 {&hf_ap_parlg
, {"PARLG", "sinecap.parlg", FT_UINT16
, BASE_HEX_DEC
, NULL
, 0x0, "Parameter length", HFILL
}},
185 {&hf_ap_datlg
, {"DATLG", "sinecap.datlg", FT_UINT16
, BASE_HEX_DEC
, NULL
, 0x0, "Data length", HFILL
}},
188 proto_ap
= proto_register_protocol (
189 "SINEC AP Telegram", /* name */
190 "SINEC AP", /* short name */
191 "sinecap" /* filter_name */
194 static int *ett
[] = {
198 proto_register_field_array(proto_ap
, hf
, array_length(hf
));
199 proto_register_subtree_array(ett
, array_length (ett
));
200 ap_heur_subdissector_list
= register_heur_dissector_list_with_description("sinecap", "SINEC AP data", proto_ap
);
204 proto_reg_handoff_ap(void)
206 heur_dissector_add("cotp", dissect_ap
, "SINEC AP Telegram over COTP", "sinecap", proto_ap
, HEURISTIC_ENABLE
);
207 heur_dissector_add("cotp_is", dissect_ap
, "SINEC AP Telegram over COTP", "sinecap_is", proto_ap
, HEURISTIC_ENABLE
);