2 * Routines for IEC 61850 GOOSE packet dissection
5 * Routines for IEC 61850 R-GOOSE packet dissection
6 * Dordije Manojlovic 2020
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * SPDX-License-Identifier: GPL-2.0-or-later
17 #include <epan/packet.h>
18 #include <epan/asn1.h>
19 #include <epan/proto_data.h>
20 #include <epan/etypes.h>
21 #include <epan/expert.h>
22 #include <wsutil/array.h>
24 #include "packet-ber.h"
25 #include "packet-acse.h"
27 #define GOOSE_PNAME "GOOSE"
28 #define GOOSE_PSNAME "GOOSE"
29 #define GOOSE_PFNAME "goose"
31 #define R_GOOSE_PNAME "R-GOOSE"
32 #define R_GOOSE_PSNAME "R-GOOSE"
33 #define R_GOOSE_PFNAME "r-goose"
35 void proto_register_goose(void);
36 void proto_reg_handoff_goose(void);
38 /* Initialize the protocol and registered fields */
39 static int proto_goose
;
40 static int proto_r_goose
;
42 static int hf_goose_session_header
;
43 static int hf_goose_spdu_id
;
44 static int hf_goose_session_hdr_length
;
45 static int hf_goose_hdr_length
;
46 static int hf_goose_content_id
;
47 static int hf_goose_spdu_lenth
;
48 static int hf_goose_spdu_num
;
49 static int hf_goose_version
;
50 static int hf_goose_security_info
;
51 static int hf_goose_current_key_t
;
52 static int hf_goose_next_key_t
;
53 static int hf_goose_key_id
;
54 static int hf_goose_init_vec_length
;
55 static int hf_goose_init_vec
;
56 static int hf_goose_session_user_info
;
57 static int hf_goose_payload
;
58 static int hf_goose_payload_length
;
59 static int hf_goose_apdu_tag
;
60 static int hf_goose_apdu_simulation
;
61 static int hf_goose_apdu_appid
;
62 static int hf_goose_apdu_length
;
63 static int hf_goose_padding_tag
;
64 static int hf_goose_padding_length
;
65 static int hf_goose_padding
;
66 static int hf_goose_hmac
;
67 static int hf_goose_appid
;
68 static int hf_goose_length
;
69 static int hf_goose_reserve1
;
70 static int hf_goose_reserve1_s_bit
;
71 static int hf_goose_reserve2
;
72 static int hf_goose_float_value
;
75 /* Bit fields in the Reserved fields */
76 #define F_RESERVE1_S_BIT 0x8000
78 /* GOOSE stored data for expert info verifications */
79 typedef struct _goose_chk_data
{
82 #define GOOSE_CHK_DATA_LEN (sizeof(goose_chk_data_t))
84 static expert_field ei_goose_mal_utctime
;
85 static expert_field ei_goose_zero_pdu
;
86 static expert_field ei_goose_invalid_sim
;
87 static expert_field ei_goose_bogus_length
;
89 #define SINGLE_FLOAT_EXP_BITS 8
90 #define FLOAT_ENC_LENGTH 5
92 #include "packet-goose-hf.c"
94 /* Initialize the subtree pointers */
95 static int ett_r_goose
;
96 static int ett_session_header
;
97 static int ett_security_info
;
98 static int ett_session_user_info
;
99 static int ett_payload
;
100 static int ett_padding
;
101 static int ett_goose
;
102 static int ett_reserve1
;
103 static int ett_expert_inf_sim
;
105 #include "packet-goose-ett.c"
107 #include "packet-goose-fn.c"
109 static dissector_handle_t goose_handle
;
112 #define OSI_SPDU_TUNNELED 0xA0 /* Tunneled */
113 #define OSI_SPDU_GOOSE 0xA1 /* GOOSE */
114 #define OSI_SPDU_SV 0xA2 /* Sample Value */
115 #define OSI_SPDU_MNGT 0xA3 /* Management */
117 static const value_string ositp_spdu_id
[] = {
118 { OSI_SPDU_TUNNELED
, "Tunneled" },
119 { OSI_SPDU_GOOSE
, "GOOSE" },
120 { OSI_SPDU_SV
, "Sample value" },
121 { OSI_SPDU_MNGT
, "Management" },
125 #define OSI_PDU_GOOSE 0x81
126 #define OSI_PDU_SV 0x82
127 #define OSI_PDU_TUNNELED 0x83
128 #define OSI_PDU_MNGT 0x84
130 static const value_string ositp_pdu_id
[] = {
131 { OSI_PDU_GOOSE
, "GOOSE" },
132 { OSI_PDU_SV
, "SV" },
133 { OSI_PDU_TUNNELED
, "Tunnel" },
134 { OSI_PDU_MNGT
, "MNGT" },
138 #define APDU_HEADER_SIZE 6
141 * Dissect GOOSE PDUs inside a PPDU.
144 dissect_goose(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
,
150 uint32_t reserve1_val
;
151 proto_item
*item
= NULL
;
152 proto_item
*tree_item
= NULL
;
153 proto_tree
*tree
= NULL
;
154 goose_chk_data_t
*data_chk
= NULL
;
156 asn1_ctx_init(&asn1_ctx
, ASN1_ENC_BER
, true, pinfo
);
158 static int * const reserve1_flags
[] = {
159 &hf_goose_reserve1_s_bit
,
163 asn1_ctx
.private_data
= wmem_alloc(pinfo
->pool
, GOOSE_CHK_DATA_LEN
);
164 data_chk
= (goose_chk_data_t
*)asn1_ctx
.private_data
;
166 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, GOOSE_PNAME
);
167 col_clear(pinfo
->cinfo
, COL_INFO
);
169 tree_item
= proto_tree_add_item(parent_tree
, proto_goose
, tvb
, 0, -1, ENC_NA
);
170 tree
= proto_item_add_subtree(tree_item
, ett_goose
);
171 add_ber_encoded_label(tvb
, pinfo
, parent_tree
);
175 proto_tree_add_item(tree
, hf_goose_appid
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
178 item
= proto_tree_add_item_ret_uint(tree
, hf_goose_length
, tvb
, offset
+ 2, 2,
179 ENC_BIG_ENDIAN
, &length
);
181 expert_add_info(pinfo
, item
, &ei_goose_bogus_length
);
183 set_actual_length(tvb
, length
);
184 proto_item_set_len(tree_item
, length
);
187 reserve1_val
= tvb_get_uint16(tvb
, offset
+ 4, ENC_BIG_ENDIAN
);
188 proto_tree_add_bitmask_value(tree
, tvb
, offset
+ 4, hf_goose_reserve1
, ett_reserve1
,
189 reserve1_flags
, reserve1_val
);
191 /* Store the header sim value for later expert info checks */
193 if(reserve1_val
& F_RESERVE1_S_BIT
){
194 data_chk
->s_bit
= true;
196 data_chk
->s_bit
= false;
202 proto_tree_add_item(tree
, hf_goose_reserve2
, tvb
, offset
+ 6, 2,
206 while (offset
< length
){
208 offset
= dissect_goose_GOOSEpdu(false, tvb
, offset
, &asn1_ctx
, tree
, -1);
209 if (offset
== old_offset
) {
210 proto_tree_add_expert(tree
, pinfo
, &ei_goose_zero_pdu
, tvb
, offset
, -1);
215 return tvb_captured_length(tvb
);
219 * Dissect RGOOSE PDUs inside ISO 8602/X.234 CLTP ConnecteionLess
220 * Transport Protocol.
223 dissect_rgoose(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
,
226 unsigned offset
= 0, old_offset
= 0;
227 uint32_t init_v_length
, payload_tag
, padding_length
, length
;
228 uint32_t payload_length
, apdu_offset
= 0, apdu_length
, apdu_simulation
;
229 proto_item
*item
= NULL
;
230 proto_tree
*tree
= NULL
, *r_goose_tree
= NULL
, *sess_user_info_tree
= NULL
;
231 goose_chk_data_t
*data_chk
= NULL
;
233 asn1_ctx_init(&asn1_ctx
, ASN1_ENC_BER
, true, pinfo
);
235 asn1_ctx
.private_data
= wmem_alloc(pinfo
->pool
, GOOSE_CHK_DATA_LEN
);
236 data_chk
= (goose_chk_data_t
*)asn1_ctx
.private_data
;
238 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, R_GOOSE_PNAME
);
239 col_clear(pinfo
->cinfo
, COL_INFO
);
241 item
= proto_tree_add_item(parent_tree
, proto_r_goose
, tvb
, 0, -1, ENC_NA
);
242 r_goose_tree
= proto_item_add_subtree(item
, ett_r_goose
);
244 /* Session header subtree */
245 item
= proto_tree_add_item(r_goose_tree
, hf_goose_session_header
, tvb
, 0,
247 tree
= proto_item_add_subtree(item
, ett_session_header
);
250 proto_tree_add_item(tree
, hf_goose_spdu_id
, tvb
, offset
++, 1,
252 /* Session header length */
253 proto_tree_add_item_ret_uint(tree
, hf_goose_session_hdr_length
, tvb
, offset
++, 1,
254 ENC_BIG_ENDIAN
, &length
);
255 proto_item_set_len(item
, length
+ 2);
257 /* Header content indicator */
258 proto_tree_add_item(tree
, hf_goose_content_id
, tvb
, offset
++, 1,
261 proto_tree_add_item(tree
, hf_goose_hdr_length
, tvb
, offset
++, 1,
264 proto_tree_add_item(tree
, hf_goose_spdu_lenth
, tvb
, offset
, 4,
268 proto_tree_add_item(tree
, hf_goose_spdu_num
, tvb
, offset
, 4,
272 proto_tree_add_item(tree
, hf_goose_version
, tvb
, offset
, 2, ENC_BIG_ENDIAN
);
275 /* Security information subtree */
276 item
= proto_tree_add_item(tree
, hf_goose_security_info
, tvb
, offset
, -1,
278 tree
= proto_item_add_subtree(item
, ett_security_info
);
279 /* Time of current key */
280 proto_tree_add_item(tree
, hf_goose_current_key_t
, tvb
, offset
, 4,
283 /* Time of next key */
284 proto_tree_add_item(tree
, hf_goose_next_key_t
, tvb
, offset
, 2,
288 proto_tree_add_item(tree
, hf_goose_key_id
, tvb
, offset
, 4, ENC_BIG_ENDIAN
);
290 /* Initialization vector length */
291 proto_tree_add_item_ret_uint(tree
, hf_goose_init_vec_length
, tvb
, offset
++, 1,
292 ENC_BIG_ENDIAN
, &init_v_length
);
293 proto_item_set_len(item
, init_v_length
+ 11);
295 if (init_v_length
> 0) {
296 /* Initialization vector bytes */
297 proto_tree_add_item(tree
, hf_goose_init_vec
, tvb
, offset
, init_v_length
,
300 offset
+= init_v_length
;
302 /* Session user information subtree */
303 item
= proto_tree_add_item(r_goose_tree
, hf_goose_session_user_info
, tvb
,
305 sess_user_info_tree
= proto_item_add_subtree(item
, ett_payload
);
307 /* Payload subtree */
308 item
= proto_tree_add_item(sess_user_info_tree
, hf_goose_payload
, tvb
,
310 tree
= proto_item_add_subtree(item
, ett_payload
);
312 proto_tree_add_item_ret_uint(tree
, hf_goose_payload_length
, tvb
, offset
, 4,
313 ENC_BIG_ENDIAN
, &payload_length
);
316 while (apdu_offset
< payload_length
){
318 proto_tree_add_item_ret_uint(tree
, hf_goose_apdu_tag
, tvb
, offset
++, 1,
319 ENC_BIG_ENDIAN
, &payload_tag
);
320 /* Simulation flag */
321 proto_tree_add_item_ret_uint(tree
, hf_goose_apdu_simulation
, tvb
, offset
++,
322 1, ENC_BIG_ENDIAN
, &apdu_simulation
);
324 proto_tree_add_item(tree
, hf_goose_apdu_appid
, tvb
, offset
, 2,
328 if (payload_tag
!= OSI_PDU_GOOSE
) {
329 return tvb_captured_length(tvb
);
332 /* Store the header sim value for later expert info checks */
335 data_chk
->s_bit
= true;
337 data_chk
->s_bit
= false;
342 proto_tree_add_item_ret_uint(tree
, hf_goose_apdu_length
, tvb
, offset
, 2,
343 ENC_BIG_ENDIAN
, &apdu_length
);
345 apdu_offset
+= (APDU_HEADER_SIZE
+ apdu_length
);
349 offset
= dissect_goose_GOOSEpdu(false, tvb
, offset
, &asn1_ctx
, tree
, -1);
350 if (offset
== old_offset
) {
351 proto_tree_add_expert(tree
, pinfo
, &ei_goose_zero_pdu
, tvb
, offset
, -1);
356 /* Check do we have padding bytes */
357 if ((tvb_captured_length(tvb
) > offset
) &&
358 (tvb_get_uint8(tvb
, offset
) == 0xAF)) {
359 /* Padding subtree */
360 item
= proto_tree_add_item(sess_user_info_tree
, hf_goose_padding
, tvb
,
362 tree
= proto_item_add_subtree(item
, ett_padding
);
365 proto_tree_add_item(tree
, hf_goose_padding_tag
, tvb
, offset
++, 1,
368 proto_tree_add_item_ret_uint(tree
, hf_goose_padding_length
, tvb
, offset
++, 1,
369 ENC_BIG_ENDIAN
, &padding_length
);
370 proto_item_set_len(item
, padding_length
+ 1);
373 proto_tree_add_item(tree
, hf_goose_padding
, tvb
, offset
, padding_length
,
375 offset
+= padding_length
;
378 /* Check do we have HMAC bytes */
379 if (tvb_captured_length(tvb
) > offset
) {
381 proto_tree_add_item(sess_user_info_tree
, hf_goose_hmac
, tvb
, offset
,
382 tvb_captured_length(tvb
) - offset
, ENC_NA
);
385 return tvb_captured_length(tvb
);
389 dissect_rgoose_heur(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
,
394 /* Check do we have at least min size of Session header bytes */
395 if (tvb_captured_length(tvb
) < 27) {
400 spdu
= tvb_get_uint8(tvb
, 0);
401 if (spdu
!= OSI_SPDU_GOOSE
) {
405 dissect_rgoose(tvb
, pinfo
, parent_tree
, data
);
409 /*--- proto_register_goose -------------------------------------------*/
410 void proto_register_goose(void) {
413 static hf_register_info hf
[] =
415 { &hf_goose_session_header
,
416 { "Session header", "rgoose.session_hdr",
417 FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
420 { "Session identifier", "rgoose.spdu_id",
421 FT_UINT8
, BASE_HEX_DEC
, VALS(ositp_spdu_id
), 0x0, NULL
, HFILL
}},
423 { &hf_goose_session_hdr_length
,
424 { "Session header length", "rgoose.session_hdr_len",
425 FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
427 { &hf_goose_content_id
,
428 { "Common session header identifier", "rgoose.common_session_id",
429 FT_UINT8
, BASE_HEX_DEC
, NULL
, 0x0, NULL
, HFILL
}},
431 { &hf_goose_hdr_length
,
432 { "Header length", "rgoose.hdr_len",
433 FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
435 { &hf_goose_spdu_lenth
,
436 { "SPDU length", "rgoose.spdu_len",
437 FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
439 { &hf_goose_spdu_num
,
440 { "SPDU number", "rgoose.spdu_num",
441 FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
444 { "Version", "rgoose.version",
445 FT_UINT16
, BASE_HEX_DEC
, NULL
, 0x0, NULL
, HFILL
}},
447 { &hf_goose_security_info
,
448 { "Security information", "rgoose.sec_info",
449 FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
451 { &hf_goose_current_key_t
,
452 { "Time of current key", "rgoose.curr_key_t",
453 FT_UINT32
, BASE_HEX_DEC
, NULL
, 0x0, NULL
, HFILL
}},
455 { &hf_goose_next_key_t
,
456 { "Time of next key", "rgoose.next_key_t",
457 FT_UINT16
, BASE_HEX_DEC
, NULL
, 0x0, NULL
, HFILL
}},
460 { "Key ID", "rgoose.key_id",
461 FT_UINT32
, BASE_HEX_DEC
, NULL
, 0x0, NULL
, HFILL
}},
463 { &hf_goose_init_vec_length
,
464 { "Initialization vector length", "rgoose.init_v_len",
465 FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
467 { &hf_goose_init_vec
,
468 { "Initialization vector", "rgoose.init_v",
469 FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
471 { &hf_goose_session_user_info
,
472 { "Session user information", "rgoose.session_user_info",
473 FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
476 { "Payload", "rgoose.payload",
477 FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
479 { &hf_goose_payload_length
,
480 { "Payload length", "rgoose.payload_len",
481 FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
483 { &hf_goose_apdu_tag
,
484 { "Payload type tag", "rgoose.pdu_tag",
485 FT_UINT8
, BASE_HEX_DEC
, VALS(ositp_pdu_id
), 0x0, NULL
, HFILL
}},
487 { &hf_goose_apdu_simulation
,
488 { "Simulation flag", "rgoose.simulation",
489 FT_UINT8
, BASE_HEX_DEC
, NULL
, 0x0, NULL
, HFILL
}},
491 { &hf_goose_apdu_appid
,
492 { "APPID", "rgoose.appid",
493 FT_UINT16
, BASE_HEX_DEC
, NULL
, 0x0, NULL
, HFILL
}},
495 { &hf_goose_apdu_length
,
496 { "APDU length", "rgoose.apdu_len",
497 FT_UINT16
, BASE_HEX_DEC
, NULL
, 0x0, NULL
, HFILL
}},
499 { &hf_goose_padding_tag
,
500 { "Padding", "rgoose.padding_tag",
501 FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
503 { &hf_goose_padding_length
,
504 { "Padding length", "rgoose.padding_len",
505 FT_UINT8
, BASE_HEX_DEC
, NULL
, 0x0, NULL
, HFILL
}},
508 { "Padding", "rgoose.padding",
509 FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
512 { "HMAC", "rgoose.hmac",
513 FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
516 { "APPID", "goose.appid",
517 FT_UINT16
, BASE_HEX_DEC
, NULL
, 0x0, NULL
, HFILL
}},
520 { "Length", "goose.length",
521 FT_UINT16
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
523 { &hf_goose_reserve1
,
524 { "Reserved 1", "goose.reserve1",
525 FT_UINT16
, BASE_HEX_DEC
, NULL
, 0x0, NULL
, HFILL
}},
527 { &hf_goose_reserve1_s_bit
,
528 { "Simulated", "goose.reserve1.s_bit",
529 FT_BOOLEAN
, 16, NULL
, F_RESERVE1_S_BIT
, NULL
, HFILL
} },
531 { &hf_goose_reserve2
,
532 { "Reserved 2", "goose.reserve2",
533 FT_UINT16
, BASE_HEX_DEC
, NULL
, 0x0, NULL
, HFILL
}},
535 { &hf_goose_float_value
,
536 { "float value", "goose.float_value",
537 FT_FLOAT
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
539 #include "packet-goose-hfarr.c"
542 /* List of subtrees */
543 static int *ett
[] = {
547 &ett_session_user_info
,
553 #include "packet-goose-ettarr.c"
556 static ei_register_info ei
[] = {
557 { &ei_goose_mal_utctime
,
558 { "goose.malformed.utctime", PI_MALFORMED
, PI_WARN
,
559 "BER Error: malformed UTCTime encoding", EXPFILL
}},
560 { &ei_goose_zero_pdu
,
561 { "goose.zero_pdu", PI_PROTOCOL
, PI_ERROR
,
562 "Internal error, zero-byte GOOSE PDU", EXPFILL
}},
563 { &ei_goose_invalid_sim
,
564 { "goose.invalid_sim", PI_PROTOCOL
, PI_WARN
,
565 "Invalid GOOSE: S bit set and Simulation attribute clear", EXPFILL
}},
566 { &ei_goose_bogus_length
,
567 { "goose.bogus_length", PI_PROTOCOL
, PI_ERROR
,
568 "GOOSE length must be at least 8 (includes header)", EXPFILL
}},
571 expert_module_t
* expert_goose
;
573 /* Register protocol */
574 proto_goose
= proto_register_protocol(GOOSE_PNAME
, GOOSE_PSNAME
, GOOSE_PFNAME
);
575 proto_r_goose
= proto_register_protocol(R_GOOSE_PNAME
, R_GOOSE_PSNAME
, R_GOOSE_PFNAME
);
577 goose_handle
= register_dissector("goose", dissect_goose
, proto_goose
);
579 /* Register fields and subtrees */
580 proto_register_field_array(proto_goose
, hf
, array_length(hf
));
581 proto_register_subtree_array(ett
, array_length(ett
));
582 expert_goose
= expert_register_protocol(proto_goose
);
583 expert_register_field_array(expert_goose
, ei
, array_length(ei
));
587 /*--- proto_reg_handoff_goose --- */
588 void proto_reg_handoff_goose(void) {
590 dissector_add_uint("ethertype", ETHERTYPE_IEC61850_GOOSE
, goose_handle
);
592 heur_dissector_add("cltp", dissect_rgoose_heur
,
593 "R-GOOSE (GOOSE over CLTP)", "rgoose_cltp", proto_goose
, HEURISTIC_ENABLE
);