/src/wireshark/epan/dissectors/packet-h1.c
Line | Count | Source |
1 | | /* packet-h1.c |
2 | | * Routines for Sinec H1 packet disassembly |
3 | | * Gerrit Gehnen <G.Gehnen@atrie.de> |
4 | | * |
5 | | * Wireshark - Network traffic analyzer |
6 | | * By Gerald Combs <gerald@wireshark.org> |
7 | | * Copyright 1998 Gerald Combs |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | |
12 | | #include "config.h" |
13 | | |
14 | | #include <epan/packet.h> |
15 | | |
16 | | void proto_register_h1(void); |
17 | | void proto_reg_handoff_h1(void); |
18 | | |
19 | | static int proto_h1; |
20 | | static int hf_h1_header; |
21 | | static int hf_h1_len; |
22 | | static int hf_h1_block_type; |
23 | | static int hf_h1_block_len; |
24 | | static int hf_h1_opcode; |
25 | | static int hf_h1_dbnr; |
26 | | static int hf_h1_dwnr; |
27 | | static int hf_h1_dlen; |
28 | | static int hf_h1_org; |
29 | | static int hf_h1_response_value; |
30 | | |
31 | | |
32 | | #define EMPTY_BLOCK 0xFF |
33 | 2 | #define OPCODE_BLOCK 0x01 |
34 | 0 | #define REQUEST_BLOCK 0x03 |
35 | 0 | #define RESPONSE_BLOCK 0x0F |
36 | | |
37 | | static const value_string block_type_vals[] = { |
38 | | { EMPTY_BLOCK, "Empty Block" }, |
39 | | { OPCODE_BLOCK, "Opcode Block" }, |
40 | | { REQUEST_BLOCK, "Request Block" }, |
41 | | { RESPONSE_BLOCK, "Response Block" }, |
42 | | {0, NULL} |
43 | | }; |
44 | | |
45 | | |
46 | | static const value_string opcode_vals[] = { |
47 | | {3, "Write Request"}, |
48 | | {4, "Write Response"}, |
49 | | {5, "Read Request"}, |
50 | | {6, "Read Response"}, |
51 | | {0, NULL} |
52 | | }; |
53 | | |
54 | | static const value_string org_vals[] = { |
55 | | {0x01, "DB"}, |
56 | | {0x02, "MB"}, |
57 | | {0x03, "EB"}, |
58 | | {0x04, "AB"}, |
59 | | {0x05, "PB"}, |
60 | | {0x06, "ZB"}, |
61 | | {0x07, "TB"}, |
62 | | {0x08, "BS"}, |
63 | | {0x09, "AS"}, |
64 | | {0x0a, "DX"}, |
65 | | {0x10, "DE"}, |
66 | | {0x11, "QB"}, |
67 | | {0, NULL} |
68 | | }; |
69 | | |
70 | | static const value_string returncode_vals[] = { |
71 | | {0x00, "No error"}, |
72 | | {0x02, "Requested block does not exist"}, |
73 | | {0x03, "Requested block too small"}, |
74 | | {0xFF, "Error, reason unknown"}, |
75 | | {0, NULL} |
76 | | }; |
77 | | |
78 | | static int ett_h1; |
79 | | static int ett_block; |
80 | | |
81 | | static bool dissect_h1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
82 | 5.42k | { |
83 | 5.42k | proto_tree *h1_tree, *block_tree; |
84 | 5.42k | proto_item *h1_ti, *block_ti; |
85 | 5.42k | int offset = 0, offset_block_start; |
86 | 5.42k | uint8_t h1_len; |
87 | 5.42k | uint8_t block_type, block_len; |
88 | 5.42k | tvbuff_t *next_tvb; |
89 | | |
90 | 5.42k | if (tvb_captured_length(tvb) < 2) { |
91 | | /* Not enough data captured to hold the "S5" header; don't try |
92 | | to interpret it as H1. */ |
93 | 173 | return false; |
94 | 173 | } |
95 | | |
96 | 5.25k | if (!(tvb_get_uint8(tvb, 0) == 'S' && tvb_get_uint8(tvb, 1) == '5')) { |
97 | 5.24k | return false; |
98 | 5.24k | } |
99 | | |
100 | 6 | col_set_str (pinfo->cinfo, COL_PROTOCOL, "H1"); |
101 | 6 | col_set_str(pinfo->cinfo, COL_INFO, "S5: "); |
102 | | |
103 | 6 | h1_ti = proto_tree_add_item(tree, proto_h1, tvb, offset, -1, ENC_NA); |
104 | 6 | h1_tree = proto_item_add_subtree(h1_ti, ett_h1); |
105 | | |
106 | 6 | proto_tree_add_item(h1_tree, hf_h1_header, tvb, offset, 2, ENC_BIG_ENDIAN); |
107 | 6 | offset += 2; |
108 | | |
109 | 6 | h1_len = tvb_get_uint8(tvb, offset); |
110 | 6 | proto_tree_add_item(h1_tree, hf_h1_len, tvb, offset, 1, ENC_BIG_ENDIAN); |
111 | 6 | proto_item_set_len(h1_ti, h1_len); |
112 | 6 | offset++; |
113 | | |
114 | 8 | while (offset < h1_len) { |
115 | 7 | offset_block_start = offset; |
116 | | |
117 | 7 | block_type = tvb_get_uint8(tvb, offset); |
118 | 7 | block_len = tvb_get_uint8(tvb, offset+1); |
119 | | |
120 | 7 | if (!try_val_to_str(block_type, block_type_vals)) { |
121 | | /* XXX - should we skip unknown blocks? */ |
122 | 4 | return false; |
123 | 4 | } |
124 | 3 | if (block_len == 0) { |
125 | | /* XXX - expert info */ |
126 | 0 | break; |
127 | 0 | } |
128 | | |
129 | 3 | block_tree = proto_tree_add_subtree_format(h1_tree, |
130 | 3 | tvb, offset, -1, ett_block, &block_ti, "%s", |
131 | 3 | val_to_str_const(block_type, block_type_vals, "Unknown block")); |
132 | | |
133 | 3 | proto_tree_add_item(block_tree, hf_h1_block_type, |
134 | 3 | tvb, offset, 1, ENC_BIG_ENDIAN); |
135 | | /* we keep increasing offset as we go though the block |
136 | | however, to find the beginning of the next block, |
137 | | we use the current block's start offset and its length field */ |
138 | 3 | offset++; |
139 | 3 | proto_tree_add_item(block_tree, hf_h1_block_len, |
140 | 3 | tvb, offset, 1, ENC_BIG_ENDIAN); |
141 | 3 | proto_item_set_len(block_ti, block_len); |
142 | 3 | offset++; |
143 | | |
144 | 3 | switch (block_type) { |
145 | 2 | case OPCODE_BLOCK: |
146 | 2 | proto_tree_add_item(block_tree, hf_h1_opcode, |
147 | 2 | tvb, offset, 1, ENC_BIG_ENDIAN); |
148 | 2 | col_append_str (pinfo->cinfo, COL_INFO, |
149 | 2 | val_to_str(pinfo->pool, tvb_get_uint8(tvb, offset), |
150 | 2 | opcode_vals, "Unknown Opcode (0x%2.2x)")); |
151 | 2 | break; |
152 | | |
153 | 0 | case REQUEST_BLOCK: |
154 | 0 | proto_tree_add_item(block_tree, hf_h1_org, tvb, |
155 | 0 | offset, 1, ENC_BIG_ENDIAN); |
156 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, " %s", |
157 | 0 | val_to_str(pinfo->pool, tvb_get_uint8(tvb, offset), |
158 | 0 | org_vals,"Unknown Type (0x%2.2x)")); |
159 | 0 | offset++; |
160 | |
|
161 | 0 | proto_tree_add_item(block_tree, hf_h1_dbnr, tvb, |
162 | 0 | offset, 1, ENC_BIG_ENDIAN); |
163 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, " %d", |
164 | 0 | tvb_get_uint8(tvb, offset)); |
165 | 0 | offset++; |
166 | |
|
167 | 0 | proto_tree_add_item(block_tree, hf_h1_dwnr, tvb, |
168 | 0 | offset, 2, ENC_BIG_ENDIAN); |
169 | 0 | col_append_fstr (pinfo->cinfo, COL_INFO, " DW %d", |
170 | 0 | tvb_get_ntohs(tvb, offset)); |
171 | 0 | offset += 2; |
172 | |
|
173 | 0 | proto_tree_add_item(block_tree, hf_h1_dlen, tvb, |
174 | 0 | offset, 2, ENC_BIG_ENDIAN); |
175 | 0 | col_append_fstr (pinfo->cinfo, COL_INFO, " Count %d", |
176 | 0 | tvb_get_ntohs(tvb, offset)); |
177 | 0 | break; |
178 | | |
179 | 0 | case RESPONSE_BLOCK: |
180 | 0 | proto_tree_add_item(block_tree, hf_h1_response_value, |
181 | 0 | tvb, offset, 1, ENC_BIG_ENDIAN); |
182 | 0 | col_append_fstr (pinfo->cinfo, COL_INFO, " %s", |
183 | 0 | val_to_str(pinfo->pool, tvb_get_uint8(tvb, offset), |
184 | 0 | returncode_vals,"Unknown Returncode (0x%2.2x")); |
185 | 0 | break; |
186 | 3 | } |
187 | | |
188 | 2 | offset = offset_block_start + block_len; /* see the comment above */ |
189 | 2 | } |
190 | | |
191 | 1 | if (tvb_reported_length_remaining(tvb, offset) > 0) { |
192 | 0 | next_tvb = tvb_new_subset_remaining(tvb, offset); |
193 | 0 | call_data_dissector(next_tvb, pinfo, tree); |
194 | 0 | } |
195 | | |
196 | 1 | return true; |
197 | 6 | } |
198 | | |
199 | | |
200 | | void |
201 | | proto_register_h1 (void) |
202 | 14 | { |
203 | 14 | static hf_register_info hf[] = { |
204 | 14 | {&hf_h1_header, |
205 | 14 | {"H1-Header", "h1.header", FT_UINT16, BASE_HEX, NULL, 0x0, |
206 | 14 | NULL, HFILL }}, |
207 | 14 | {&hf_h1_len, |
208 | 14 | {"Length indicator", "h1.len", FT_UINT16, BASE_DEC, NULL, 0x0, |
209 | 14 | NULL, HFILL }}, |
210 | 14 | {&hf_h1_block_type, |
211 | 14 | {"Block type", "h1.block_type", FT_UINT8, BASE_HEX, VALS(block_type_vals), 0x0, |
212 | 14 | NULL, HFILL }}, |
213 | 14 | {&hf_h1_block_len, |
214 | 14 | {"Block length", "h1.block_len", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, |
215 | 14 | {&hf_h1_opcode, |
216 | 14 | {"Opcode", "h1.opcode", FT_UINT8, BASE_HEX, VALS (opcode_vals), 0x0, |
217 | 14 | NULL, HFILL }}, |
218 | 14 | {&hf_h1_org, |
219 | 14 | {"Memory type", "h1.org", FT_UINT8, BASE_HEX, VALS (org_vals), 0x0, |
220 | 14 | NULL, HFILL }}, |
221 | 14 | {&hf_h1_dbnr, |
222 | 14 | {"Memory block number", "h1.dbnr", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, |
223 | 14 | {&hf_h1_dwnr, |
224 | 14 | {"Address within memory block", "h1.dwnr", FT_UINT16, BASE_DEC, NULL, 0x0, |
225 | 14 | NULL, HFILL }}, |
226 | 14 | {&hf_h1_dlen, |
227 | 14 | {"Length in words", "h1.dlen", FT_INT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, |
228 | 14 | {&hf_h1_response_value, |
229 | 14 | {"Response value", "h1.resvalue", FT_UINT8, BASE_DEC, |
230 | 14 | VALS (returncode_vals), 0x0, NULL, HFILL }} |
231 | 14 | }; |
232 | | |
233 | 14 | static int *ett[] = { |
234 | 14 | &ett_h1, |
235 | 14 | &ett_block, |
236 | 14 | }; |
237 | | |
238 | 14 | proto_h1 = proto_register_protocol ("Sinec H1 Protocol", "H1", "h1"); |
239 | 14 | proto_register_field_array (proto_h1, hf, array_length (hf)); |
240 | 14 | proto_register_subtree_array (ett, array_length (ett)); |
241 | 14 | } |
242 | | |
243 | | void |
244 | | proto_reg_handoff_h1(void) |
245 | 14 | { |
246 | 14 | heur_dissector_add("cotp", dissect_h1, |
247 | 14 | "Sinec H1 over COTP", "hi_cotp", proto_h1, HEURISTIC_ENABLE); |
248 | 14 | heur_dissector_add("cotp_is", dissect_h1, |
249 | 14 | "Sinec H1 over COTP (inactive subset)", "hi_cotp_is", proto_h1, HEURISTIC_ENABLE); |
250 | 14 | heur_dissector_add("tcp", dissect_h1, |
251 | 14 | "Sinec H1 over TCP", "hi_tcp", proto_h1, HEURISTIC_ENABLE); |
252 | 14 | } |
253 | | |
254 | | /* |
255 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
256 | | * |
257 | | * Local Variables: |
258 | | * c-basic-offset: 4 |
259 | | * tab-width: 8 |
260 | | * indent-tabs-mode: nil |
261 | | * End: |
262 | | * |
263 | | * ex: set shiftwidth=4 tabstop=8 expandtab: |
264 | | * :indentSize=4:tabSize=8:noTabs=true: |
265 | | */ |