/src/wireshark/epan/dissectors/packet-5co-legacy.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* packet-5co-legacy.c |
2 | | * Routines for FiveCo's Legacy Register Access Protocol dissector |
3 | | * Copyright 2021, Antoine Gardiol <antoine.gardiol@fiveco.ch> |
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 | | /* |
13 | | * This protocol allows access to FiveCo's Ethernet products registers with old legacy |
14 | | * protocol. Product list can be found under https://www.fiveco.ch/bus-converter-products.html. |
15 | | * Protocol description can be found (by example) in FMod-TCP xx manual that can be dowloaded from |
16 | | * https://www.fiveco.ch/product-fmod-tcp-db.html. |
17 | | * Note that this protocol is a question-answer protocol. It's header is composed of: |
18 | | * - 16 bits type |
19 | | * - 16 bits frame id |
20 | | * - 16 bits length of parameters (n) |
21 | | * - n bytes of parameters (depends upon packet type) |
22 | | * - 16 bits IP like checksum |
23 | | * |
24 | | * This build-in dissector is replacing a plugin dissector available from Wireshark 1.8. |
25 | | */ |
26 | | |
27 | | #include <config.h> |
28 | | #include <epan/packet.h> |
29 | | #include <epan/proto_data.h> |
30 | | #include <wsutil/array.h> |
31 | | #include "packet-tcp.h" |
32 | | #include <stdio.h> |
33 | | #include <stdbool.h> |
34 | | #include <stdint.h> |
35 | | /* Prototypes */ |
36 | | void proto_reg_handoff_FiveCoLegacy(void); |
37 | | void proto_register_FiveCoLegacy(void); |
38 | | |
39 | | static dissector_handle_t FiveCoLegacy_handle; |
40 | | |
41 | | /****************************************************************************/ |
42 | | /* Definition declaration */ |
43 | | /****************************************************************************/ |
44 | | |
45 | | // Protocol header length and frame minimum length |
46 | 0 | #define FIVECO_LEGACY_HEADER_LENGTH 6 |
47 | 0 | #define FIVECO_LEGACY_MIN_LENGTH FIVECO_LEGACY_HEADER_LENGTH + 2 // Checksum is 16 bits |
48 | | |
49 | 14 | #define PSNAME "5co-legacy" |
50 | | |
51 | | /* Global sample ports preferences */ |
52 | 14 | #define FIVECO_PORT1 8010 /* TCP port of the FiveCo protocol */ |
53 | 14 | #define FIVECO_PORT2 8004 /* TCP port of the FiveCo protocol for web page upload */ |
54 | 14 | #define FIVECO_UDP_PORT1 7010 /* UDP port of the FiveCo protocol */ |
55 | | |
56 | | /* 16 bits type known available functions */ |
57 | | enum fiveco_functions |
58 | | { |
59 | | I2C_READ = 0x0001, |
60 | | I2C_WRITE, |
61 | | I2C_READ_ANSWER, |
62 | | I2C_WRITE_ANSWER, |
63 | | I2C_SCAN, |
64 | | I2C_SCAN_ANSWER, |
65 | | I2C_READ_WRITE_ACK, |
66 | | I2C_READ_WRITE_ACK_ANSWER, |
67 | | I2C_READ_WRITE_ACK_ERROR, |
68 | | READ_REGISTER = 0x0021, |
69 | | WRITE_REGISTER, |
70 | | READ_REGISTER_ANSWER, |
71 | | WRITE_REGISTER_ANSWER, |
72 | | WRITE_REGISTER_QUIET, |
73 | | EASY_IP_ADDRESS_CONFIG = 0x002A, |
74 | | EASY_IP_ADDRESS_CONFIG_ANSWER, |
75 | | FLASH_AREA_ERASE = 0x0031, |
76 | | FLASH_AREA_LOAD, |
77 | | FLASH_AREA_ANSWER |
78 | | }; |
79 | | |
80 | | /* Forward references to functions */ |
81 | | static uint16_t |
82 | | checksum_fiveco(tvbuff_t * byte_tab, uint16_t start_offset, uint16_t size); |
83 | | static int fiveco_hash_equal(const void *v, const void *w); |
84 | | |
85 | | /* Register decoding functions prototypes */ |
86 | | static void dispType( char *result, uint32_t type); |
87 | | static void dispVersion( char *result, uint32_t type); |
88 | | static void dispMAC( char *result, uint64_t type); |
89 | | static void dispIP( char *result, uint32_t type); |
90 | | static void dispMask( char *result, uint32_t type); |
91 | | static void dispTimeout( char *result, uint32_t type); |
92 | | |
93 | | /* Initialize the protocol and registered fields */ |
94 | | static int proto_FiveCoLegacy; /* Wireshark ID of the FiveCo protocol */ |
95 | | |
96 | | /* static dissector_handle_t data_handle = NULL; */ |
97 | | static int hf_fiveco_header; /* The following hf_* variables are used to hold the Wireshark IDs of */ |
98 | | static int hf_fiveco_fct; /* our header fields; they are filled out when we call */ |
99 | | static int hf_fiveco_id; /* proto_register_field_array() in proto_register_fiveco() */ |
100 | | static int hf_fiveco_length; |
101 | | static int hf_fiveco_data; |
102 | | static int hf_fiveco_cks; |
103 | | static int hf_fiveco_i2cadd; |
104 | | static int hf_fiveco_i2c2write; |
105 | | static int hf_fiveco_i2cwrite; |
106 | | static int hf_fiveco_i2c2read; |
107 | | static int hf_fiveco_i2c2scan; |
108 | | static int hf_fiveco_i2canswer; |
109 | | static int hf_fiveco_i2cwriteanswer; |
110 | | static int hf_fiveco_i2cscaned; |
111 | | static int hf_fiveco_i2cerror; |
112 | | static int hf_fiveco_i2cack; |
113 | | static int hf_fiveco_regread; |
114 | | static int hf_fiveco_regreadunknown; |
115 | | static int hf_fiveco_regreaduk; |
116 | | static int hf_fiveco_EasyIPMAC; |
117 | | static int hf_fiveco_EasyIPIP; |
118 | | static int hf_fiveco_EasyIPSM; |
119 | | |
120 | | static int ett_fiveco_header; /* These are the ids of the subtrees that we may be creating */ |
121 | | static int ett_fiveco_data; /* for the header fields. */ |
122 | | static int ett_fiveco; |
123 | | static int ett_fiveco_checksum; |
124 | | |
125 | | /* Constants declaration */ |
126 | | static const value_string packettypenames[] = { |
127 | | {I2C_READ, "I2C Read (deprecated)"}, |
128 | | {I2C_READ_ANSWER, "I2C Read Answer (deprecated)"}, |
129 | | {I2C_WRITE, "I2C Write (deprecated)"}, |
130 | | {I2C_WRITE_ANSWER, "I2C Write Answer (deprecated)"}, |
131 | | {I2C_SCAN, "I2C Scan"}, |
132 | | {I2C_SCAN_ANSWER, "I2C Scan Answer"}, |
133 | | {I2C_READ_WRITE_ACK, "I2C Read and write with ack"}, |
134 | | {I2C_READ_WRITE_ACK_ANSWER, "I2C Read and write with ack Answer"}, |
135 | | {I2C_READ_WRITE_ACK_ERROR, "I2C Read and write error"}, |
136 | | {READ_REGISTER, "Read register"}, |
137 | | {READ_REGISTER_ANSWER, "Read register Answer"}, |
138 | | {WRITE_REGISTER, "Write register"}, |
139 | | {WRITE_REGISTER_ANSWER, "Write register Answer"}, |
140 | | {WRITE_REGISTER_QUIET, "Write register (no answer wanted)"}, |
141 | | {EASY_IP_ADDRESS_CONFIG, "Easy IP address config"}, |
142 | | {EASY_IP_ADDRESS_CONFIG_ANSWER, "Easy IP address config Acknowledge"}, |
143 | | {FLASH_AREA_ERASE, "Flash area Erase"}, |
144 | | {FLASH_AREA_LOAD, "Flash area Upload"}, |
145 | | {FLASH_AREA_ANSWER, "Flash area Answer"}, |
146 | | {0, NULL}}; |
147 | | |
148 | | /* Conversation request key structure */ |
149 | | typedef struct |
150 | | { |
151 | | uint32_t conversation; |
152 | | uint64_t unInternalID; |
153 | | uint16_t usExpCmd; |
154 | | } FCOSConvRequestKey; |
155 | | |
156 | | /* Conversation request value structure */ |
157 | | typedef struct |
158 | | { |
159 | | uint16_t usParaLen; |
160 | | uint16_t isReplied; |
161 | | uint8_t *pDataBuffer; |
162 | | } FCOSConvRequestVal; |
163 | | |
164 | | /* Conversation hash tables */ |
165 | | static wmem_map_t *FiveCo_requests_hash; |
166 | | |
167 | | /* Internal unique ID (used to match answer with question |
168 | | since some software set always 0 as packet ID in protocol header) |
169 | | */ |
170 | | static uint64_t g_unInternalID; |
171 | | |
172 | | /* Register definition structure (used to detect known registers when it is possible) */ |
173 | | typedef struct |
174 | | { |
175 | | uint32_t unValue; // Register address |
176 | | uint32_t unSize; // Register size (in bytes) |
177 | | const char *name; // Register name |
178 | | const char *abbrev; // Abbreviation for header fill |
179 | | const enum ftenum ft; // Field type |
180 | | int nsWsHeaderID; // Wireshark ID for header fill |
181 | | const void *pFct; // Conversion function |
182 | | } FCOSRegisterDef; |
183 | | |
184 | | /* Known (common on every product) registers */ |
185 | | static FCOSRegisterDef aRegisters[] = { |
186 | | {0x00, 4, "Register Type/Model", "5co_legacy.RegTypeModel", FT_UINT32, -1, CF_FUNC(dispType)}, |
187 | | {0x01, 4, "Register Version", "5co_legacy.RegVersion", FT_UINT32, -1, CF_FUNC(dispVersion)}, |
188 | | {0x02, 0, "Function Reset device", "5co_legacy.RegReset", FT_NONE, -1, NULL}, |
189 | | {0x03, 0, "Function Save user parameters", "5co_legacy.RegSave", FT_NONE, -1, NULL}, |
190 | | {0x04, 0, "Function Restore user parameters", "5co_legacy.RegRestore", FT_NONE, -1, NULL}, |
191 | | {0x05, 0, "Function Restore factory parameters", "5co_legacy.RegRestoreFact", FT_NONE, -1, NULL}, |
192 | | {0x06, 0, "Function Save factory parameters", "5co_legacy.SaveFact", FT_NONE, -1, NULL}, |
193 | | {0x07, 0, "Register unknown", "5co_legacy.RegUnknown07", FT_NONE, -1, NULL}, |
194 | | {0x08, 0, "Register unknown", "5co_legacy.RegUnknown08", FT_NONE, -1, NULL}, |
195 | | {0x09, 0, "Register unknown", "5co_legacy.RegUnknown09", FT_NONE, -1, NULL}, |
196 | | {0x0A, 0, "Register unknown", "5co_legacy.RegUnknown0A", FT_NONE, -1, NULL}, |
197 | | {0x0B, 0, "Register unknown", "5co_legacy.RegUnknown0B", FT_NONE, -1, NULL}, |
198 | | {0x0C, 0, "Register unknown", "5co_legacy.RegUnknown0C", FT_NONE, -1, NULL}, |
199 | | {0x0D, 0, "Register unknown", "5co_legacy.RegUnknown0D", FT_NONE, -1, NULL}, |
200 | | {0x0E, 0, "Register unknown", "5co_legacy.RegUnknown0E", FT_NONE, -1, NULL}, |
201 | | {0x0F, 0, "Register unknown", "5co_legacy.RegUnknown0F", FT_NONE, -1, NULL}, |
202 | | {0x10, 4, "Register Communication options", "5co_legacy.RegComOption", FT_UINT32, -1, NULL}, |
203 | | {0x11, 6, "Register Ethernet MAC Address", "5co_legacy.RegMAC", FT_UINT48, -1, CF_FUNC(dispMAC)}, |
204 | | {0x12, 4, "Register IP Address", "5co_legacy.RegIPAdd", FT_UINT32, -1, CF_FUNC(dispIP)}, |
205 | | {0x13, 4, "Register IP Mask", "5co_legacy.RegIPMask", FT_UINT32, -1, CF_FUNC(dispMask)}, |
206 | | {0x14, 1, "Register TCP Timeout", "5co_legacy.RegTCPTimeout", FT_UINT8, -1, CF_FUNC(dispTimeout)}, |
207 | | {0x15, 16, "Register Module name", "5co_legacy.RegName", FT_STRING, -1, NULL}}; |
208 | | |
209 | | /* List of static header fields */ |
210 | | static hf_register_info hf_base[] = { |
211 | | {&hf_fiveco_header, {"Header", "5co_legacy.header", FT_NONE, BASE_NONE, NULL, 0x0, "Header of the packet", HFILL}}, |
212 | | {&hf_fiveco_fct, {"Function", "5co_legacy.fct", FT_UINT16, BASE_HEX, VALS(packettypenames), 0x0, "Function type", HFILL}}, |
213 | | {&hf_fiveco_id, {"Frame ID", "5co_legacy.id", FT_UINT16, BASE_DEC, NULL, 0x0, "Packet ID", HFILL}}, |
214 | | {&hf_fiveco_length, {"Data length", "5co_legacy.length", FT_UINT16, BASE_DEC, NULL, 0x0, "Parameters length of the packet", HFILL}}, |
215 | | {&hf_fiveco_data, {"Data", "5co_legacy.data", FT_NONE, BASE_NONE, NULL, 0x0, "Data (parameters)", HFILL}}, |
216 | | {&hf_fiveco_cks, {"Checksum", "5co_legacy.checksum", FT_UINT16, BASE_HEX, NULL, 0x0, "Checksum of the packet", HFILL}}, |
217 | | {&hf_fiveco_i2cadd, {"I2C Address", "5co_legacy.i2cadd", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL}}, |
218 | | {&hf_fiveco_i2c2write, {"I2C number of bytes to write", "5co_legacy.i2c2write", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL}}, |
219 | | {&hf_fiveco_i2cwrite, {"I2C bytes to write", "5co_legacy.i2cwrite", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
220 | | {&hf_fiveco_i2c2read, {"I2C number of bytes to read", "5co_legacy.i2c2read", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL}}, |
221 | | {&hf_fiveco_i2canswer, {"I2C bytes read", "5co_legacy.i2cread", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
222 | | {&hf_fiveco_i2cwriteanswer, {"I2C bytes write", "5co_legacy.i2writeanswer", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
223 | | {&hf_fiveco_i2cack, {"I2C ack state", "5co_legacy.i2cack", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}}, |
224 | | {&hf_fiveco_i2c2scan, {"I2C addresses to scan", "5co_legacy.i2c2scan", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
225 | | {&hf_fiveco_i2cscaned, {"I2C addresses present", "5co_legacy.i2cscaned", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
226 | | {&hf_fiveco_i2cerror, {"I2C error", "5co_legacy.i2cerror", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
227 | | {&hf_fiveco_regread, {"Read", "5co_legacy.regread", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
228 | | {&hf_fiveco_regreadunknown, {"Read Register unknown", "5co_legacy.hf_fiveco_regreadunknown", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
229 | | {&hf_fiveco_regreaduk, {"Data not decoded", "5co_legacy.regreaduk", FT_NONE, BASE_NONE, NULL, 0x0, "Data not decoded because there are unable to map to a known register", HFILL}}, |
230 | | {&hf_fiveco_EasyIPMAC, {"MAC address", "5co_legacy.EasyIPMAC", FT_ETHER, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
231 | | {&hf_fiveco_EasyIPIP, {"New IP address", "5co_legacy.EasyIPIP", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL}}, |
232 | | {&hf_fiveco_EasyIPSM, {"New subnet mask", "5co_legacy.EasyIPSM", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL}} |
233 | | }; |
234 | | |
235 | | /*****************************************************************************/ |
236 | | /* Code to actually dissect the packets */ |
237 | | /* Callback function for reassembled packet */ |
238 | | /*****************************************************************************/ |
239 | | static int |
240 | | dissect_FiveCoLegacy(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
241 | 0 | { |
242 | 0 | uint16_t checksum_cal, checksum_rx; |
243 | 0 | uint16_t i, j, y; |
244 | 0 | uint16_t tcp_data_offset = 0; |
245 | 0 | uint32_t tcp_data_length = 0; |
246 | 0 | uint16_t header_type = 0; |
247 | 0 | uint16_t header_id = 0; |
248 | 0 | uint16_t header_data_length = 0; |
249 | 0 | uint8_t data_i2c_length = 0; |
250 | 0 | proto_item *fiveco_item = NULL; |
251 | 0 | proto_item *fiveco_header_item = NULL; |
252 | 0 | proto_item *fiveco_data_item = NULL; |
253 | 0 | proto_tree *fiveco_tree = NULL; |
254 | 0 | proto_tree *fiveco_header_tree = NULL; |
255 | 0 | proto_tree *fiveco_data_tree = NULL; |
256 | 0 | conversation_t *conversation; |
257 | 0 | bool isRequest = false; |
258 | 0 | uint64_t *pulInternalID = NULL; |
259 | 0 | FCOSConvRequestKey requestKey, *pNewRequestKey; |
260 | 0 | FCOSConvRequestVal *pRequestVal = NULL; |
261 | 0 | tvbuff_t *pRequestTvb = NULL; |
262 | 0 | uint8_t ucAdd, ucBytesToWrite, ucBytesToRead; |
263 | 0 | uint8_t ucRegAdd, ucRegSize; |
264 | 0 | uint32_t unOffset; |
265 | 0 | uint32_t unSize; |
266 | | |
267 | | /* Load protocol payload length (including checksum) */ |
268 | 0 | tcp_data_length = tvb_captured_length(tvb); |
269 | 0 | if (tcp_data_length < FIVECO_LEGACY_MIN_LENGTH) // Check checksum presence |
270 | 0 | return 0; |
271 | | |
272 | | /* Display fiveco in protocol column */ |
273 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, PSNAME); |
274 | | /* Clear out stuff in the info column */ |
275 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
276 | | |
277 | | /* Look for all future TCP conversations between the |
278 | | * requestiong server and the FiveCo device using the |
279 | | * same src & dest addr and ports. |
280 | | */ |
281 | 0 | conversation = find_or_create_conversation(pinfo); |
282 | 0 | requestKey.conversation = conversation->conv_index; |
283 | | |
284 | | /* Loop because several fiveco packets can be present in one TCP packet */ |
285 | 0 | while (tcp_data_offset < tcp_data_length) { |
286 | | |
287 | | /* Check that header type is correct */ |
288 | 0 | header_type = tvb_get_ntohs(tvb, tcp_data_offset + 0); |
289 | 0 | if (try_val_to_str(header_type, packettypenames) == NULL) |
290 | 0 | return 0; |
291 | | |
292 | | /* Read packet ID */ |
293 | 0 | header_id = tvb_get_ntohs(tvb, tcp_data_offset + 2); |
294 | | |
295 | | /* Check that there's enough data versus prot data header_data_length */ |
296 | 0 | header_data_length = tvb_get_ntohs(tvb, tcp_data_offset + 4); |
297 | 0 | if (header_data_length > tcp_data_length - tcp_data_offset - 8) { |
298 | 0 | return 0; |
299 | 0 | } |
300 | | |
301 | | /* Get/Set internal ID for this packet number */ |
302 | 0 | pulInternalID = (uint64_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_FiveCoLegacy, pinfo->num); |
303 | | /* If internal ID is not set (null), create it */ |
304 | 0 | if (!pulInternalID) |
305 | 0 | { |
306 | | /* If it is a new request, increment internal ID */ |
307 | 0 | if ((header_type == I2C_READ) || (header_type == I2C_WRITE) || (header_type == I2C_SCAN) || |
308 | 0 | (header_type == I2C_READ_WRITE_ACK) || (header_type == READ_REGISTER) || (header_type == WRITE_REGISTER)) |
309 | 0 | { |
310 | 0 | isRequest = true; |
311 | 0 | g_unInternalID++; // Increment unique request ID and record it in the new request |
312 | | /* Note: Since some software do not increment packet id located in frame header |
313 | | we use an internal ID to match answers to request. */ |
314 | 0 | } |
315 | 0 | pulInternalID = wmem_new(wmem_file_scope(), uint64_t); |
316 | 0 | *pulInternalID = g_unInternalID; |
317 | 0 | p_add_proto_data(wmem_file_scope(), pinfo, proto_FiveCoLegacy, pinfo->num, pulInternalID); |
318 | 0 | } |
319 | | |
320 | | /* Get info about the request */ |
321 | 0 | requestKey.usExpCmd = header_type; |
322 | 0 | requestKey.unInternalID = *pulInternalID; |
323 | 0 | pRequestVal = (FCOSConvRequestVal *)wmem_map_lookup(FiveCo_requests_hash, &requestKey); |
324 | 0 | if ((!pinfo->fd->visited) && (!pRequestVal) && (isRequest)) |
325 | 0 | { |
326 | | /* If unknown and if it is a request, allocate new hash element that we want to handle later in answer */ |
327 | 0 | pNewRequestKey = wmem_new(wmem_file_scope(), FCOSConvRequestKey); |
328 | 0 | *pNewRequestKey = requestKey; |
329 | 0 | pNewRequestKey->unInternalID = g_unInternalID; |
330 | 0 | switch (header_type) |
331 | 0 | { |
332 | 0 | case I2C_READ: |
333 | 0 | pNewRequestKey->usExpCmd = I2C_READ_ANSWER; |
334 | 0 | break; |
335 | 0 | case I2C_WRITE: |
336 | 0 | pNewRequestKey->usExpCmd = I2C_WRITE_ANSWER; |
337 | 0 | break; |
338 | 0 | case I2C_SCAN: |
339 | 0 | pNewRequestKey->usExpCmd = I2C_SCAN_ANSWER; |
340 | 0 | break; |
341 | 0 | case I2C_READ_WRITE_ACK: |
342 | 0 | pNewRequestKey->usExpCmd = I2C_READ_WRITE_ACK_ANSWER; |
343 | 0 | break; |
344 | 0 | case READ_REGISTER: |
345 | 0 | pNewRequestKey->usExpCmd = READ_REGISTER_ANSWER; |
346 | 0 | break; |
347 | 0 | } |
348 | | |
349 | 0 | pRequestVal = wmem_new(wmem_file_scope(), FCOSConvRequestVal); |
350 | 0 | pRequestVal->usParaLen = header_data_length; |
351 | 0 | pRequestVal->isReplied = false; |
352 | 0 | pRequestVal->pDataBuffer = (uint8_t *)wmem_alloc(wmem_file_scope(), header_data_length); |
353 | 0 | tvb_memcpy(tvb, pRequestVal->pDataBuffer, tcp_data_offset + 6, header_data_length); |
354 | |
|
355 | 0 | wmem_map_insert(FiveCo_requests_hash, pNewRequestKey, pRequestVal); |
356 | 0 | } |
357 | | |
358 | 0 | if (pRequestVal) { |
359 | 0 | pRequestTvb = tvb_new_child_real_data(tvb, pRequestVal->pDataBuffer, pRequestVal->usParaLen, pRequestVal->usParaLen); |
360 | 0 | } |
361 | | |
362 | | /* Compute checksum of the packet and read one received */ |
363 | 0 | checksum_cal = checksum_fiveco(tvb, tcp_data_offset, header_data_length + 6); |
364 | 0 | checksum_rx = tvb_get_ntohs(tvb, tcp_data_offset + header_data_length + 6); |
365 | | |
366 | | /* Add text to info column */ |
367 | | /* If the offset != 0 (not first fiveco frame in tcp packet) add a comma in info column */ |
368 | 0 | if (tcp_data_offset != 0) |
369 | 0 | { |
370 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, ", %s ID=%d Len=%d", |
371 | 0 | val_to_str(header_type, packettypenames, "Unknown Type:0x%02x"), header_id, header_data_length); |
372 | 0 | } |
373 | 0 | else |
374 | 0 | { |
375 | 0 | col_append_fstr(pinfo->cinfo, COL_INFO, "%s ID=%d Len=%d", |
376 | 0 | val_to_str(header_type, packettypenames, "Unknown Type:0x%02x"), header_id, header_data_length); |
377 | 0 | } |
378 | |
|
379 | 0 | if (checksum_rx != checksum_cal) |
380 | 0 | { |
381 | 0 | col_append_str(pinfo->cinfo, COL_INFO, " [BAD CHECKSUM !!]"); |
382 | 0 | } |
383 | | |
384 | | /* Add FiveCo protocol in tree (after TCP or UDP entry) */ |
385 | 0 | fiveco_item = proto_tree_add_item(tree, proto_FiveCoLegacy, tvb, tcp_data_offset + 0, |
386 | 0 | header_data_length + 8, ENC_NA); /* Add a new entry inside tree display */ |
387 | 0 | proto_item_append_text(fiveco_item, " (%s)", val_to_str(header_type, packettypenames, "Unknown Type:0x%02x")); |
388 | | |
389 | | /* Add fiveco Protocol tree and sub trees for Header, Data and Checksum */ |
390 | 0 | fiveco_tree = proto_item_add_subtree(fiveco_item, ett_fiveco); // FiveCo prot tree |
391 | 0 | fiveco_header_item = proto_tree_add_item(fiveco_tree, hf_fiveco_header, |
392 | 0 | tvb, tcp_data_offset + 0, 6, ENC_NA); // Header tree |
393 | 0 | fiveco_header_tree = proto_item_add_subtree(fiveco_header_item, ett_fiveco_header); |
394 | 0 | proto_tree_add_item(fiveco_header_tree, hf_fiveco_fct, |
395 | 0 | tvb, tcp_data_offset + 0, 2, ENC_BIG_ENDIAN); // Packet type (function) in Header |
396 | 0 | proto_tree_add_item(fiveco_header_tree, hf_fiveco_id, |
397 | 0 | tvb, tcp_data_offset + 2, 2, ENC_BIG_ENDIAN); // Packet ID in Header |
398 | 0 | proto_tree_add_item(fiveco_header_tree, hf_fiveco_length, |
399 | 0 | tvb, tcp_data_offset + 4, 2, ENC_BIG_ENDIAN); // Length of para in Header |
400 | |
|
401 | 0 | tcp_data_offset += 6; // put offset on start of data (parameters) |
402 | | |
403 | | // If there are parameters (data) in packet, display them in data sub tree |
404 | 0 | if (header_data_length > 0) |
405 | 0 | { |
406 | 0 | fiveco_data_item = proto_tree_add_item(fiveco_tree, hf_fiveco_data, tvb, tcp_data_offset, |
407 | 0 | header_data_length, ENC_NA); // Data tree |
408 | 0 | fiveco_data_tree = proto_item_add_subtree(fiveco_data_item, ett_fiveco_data); |
409 | 0 | switch (header_type) |
410 | 0 | { |
411 | 0 | case I2C_READ: |
412 | 0 | case I2C_READ_WRITE_ACK: |
413 | 0 | i = 0; |
414 | 0 | while (i < header_data_length) |
415 | 0 | { |
416 | 0 | proto_tree_add_item(fiveco_data_tree, hf_fiveco_i2cadd, tvb, tcp_data_offset + i, 1, ENC_BIG_ENDIAN); |
417 | 0 | i += 1; |
418 | 0 | data_i2c_length = tvb_get_uint8(tvb, tcp_data_offset + i); |
419 | 0 | proto_tree_add_item(fiveco_data_tree, hf_fiveco_i2c2write, tvb, tcp_data_offset + i, 1, ENC_BIG_ENDIAN); |
420 | 0 | i += 1; |
421 | 0 | fiveco_data_item = proto_tree_add_item(fiveco_data_tree, hf_fiveco_i2cwrite, |
422 | 0 | tvb, tcp_data_offset + i, data_i2c_length, ENC_NA); |
423 | 0 | proto_item_append_text(fiveco_data_item, ": "); |
424 | 0 | for (j = 0; j < data_i2c_length; j++) |
425 | 0 | { |
426 | 0 | proto_item_append_text(fiveco_data_item, "0x%.2X ", |
427 | 0 | tvb_get_uint8(tvb, tcp_data_offset + i)); |
428 | 0 | i += 1; |
429 | 0 | } |
430 | 0 | proto_tree_add_item(fiveco_data_tree, hf_fiveco_i2c2read, tvb, tcp_data_offset + i, 1, ENC_BIG_ENDIAN); |
431 | 0 | i += 1; |
432 | 0 | } |
433 | 0 | break; |
434 | 0 | case I2C_WRITE: |
435 | 0 | i = 0; |
436 | 0 | while (i < header_data_length) |
437 | 0 | { |
438 | 0 | proto_tree_add_item(fiveco_data_tree, hf_fiveco_i2cadd, tvb, tcp_data_offset + i, 1, ENC_BIG_ENDIAN); |
439 | 0 | i += 1; |
440 | 0 | data_i2c_length = tvb_get_uint8(tvb, tcp_data_offset + i); |
441 | 0 | proto_tree_add_item(fiveco_data_tree, hf_fiveco_i2c2write, tvb, tcp_data_offset + i, 1, ENC_BIG_ENDIAN); |
442 | 0 | i += 1; |
443 | 0 | fiveco_data_item = proto_tree_add_item(fiveco_data_tree, hf_fiveco_i2cwrite, |
444 | 0 | tvb, tcp_data_offset + i, data_i2c_length, ENC_NA); |
445 | 0 | proto_item_append_text(fiveco_data_item, ": "); |
446 | 0 | for (j = 0; j < data_i2c_length; j++) |
447 | 0 | { |
448 | 0 | proto_item_append_text(fiveco_data_item, "0x%.2X ", |
449 | 0 | tvb_get_uint8(tvb, tcp_data_offset + i)); |
450 | 0 | i += 1; |
451 | 0 | } |
452 | 0 | } |
453 | 0 | break; |
454 | 0 | case I2C_SCAN: |
455 | 0 | fiveco_data_item = proto_tree_add_item(fiveco_data_tree, hf_fiveco_i2c2scan, |
456 | 0 | tvb, tcp_data_offset + 0, header_data_length, ENC_NA); |
457 | 0 | proto_item_append_text(fiveco_data_item, ": "); |
458 | | // If specific address exists in packet, display them |
459 | 0 | for (i = 0; i < header_data_length; i++) |
460 | 0 | { |
461 | 0 | proto_item_append_text(fiveco_data_item, "0x%.2X ", |
462 | 0 | tvb_get_uint8(tvb, tcp_data_offset + i)); |
463 | 0 | } |
464 | 0 | break; |
465 | 0 | case I2C_SCAN_ANSWER: |
466 | 0 | fiveco_data_item = proto_tree_add_item(fiveco_data_tree, hf_fiveco_i2cscaned, |
467 | 0 | tvb, tcp_data_offset + 0, header_data_length, ENC_NA); |
468 | 0 | proto_item_append_text(fiveco_data_item, ": "); |
469 | | // Display slave address presents in answer |
470 | 0 | for (i = 0; i < header_data_length; i++) |
471 | 0 | { |
472 | 0 | proto_item_append_text(fiveco_data_item, "0x%.2X ", |
473 | 0 | tvb_get_uint8(tvb, tcp_data_offset + i)); |
474 | 0 | } |
475 | 0 | break; |
476 | 0 | case I2C_READ_WRITE_ACK_ERROR: |
477 | 0 | fiveco_data_item = proto_tree_add_item(fiveco_data_tree, hf_fiveco_i2cerror, |
478 | 0 | tvb, tcp_data_offset + 0, header_data_length, ENC_NA); |
479 | 0 | proto_item_append_text(fiveco_data_item, ": "); |
480 | 0 | proto_item_append_text(fiveco_data_item, "0x%.2X ", |
481 | 0 | tvb_get_uint8(tvb, tcp_data_offset)); |
482 | 0 | break; |
483 | 0 | case READ_REGISTER: |
484 | | // List registers asked for read |
485 | 0 | for (i = 0; i < header_data_length; i++) |
486 | 0 | { |
487 | 0 | ucRegAdd = tvb_get_uint8(tvb, tcp_data_offset + i); |
488 | 0 | if ((ucRegAdd < array_length(aRegisters)) && |
489 | 0 | (aRegisters[ucRegAdd].unValue == ucRegAdd)) |
490 | 0 | { |
491 | 0 | fiveco_data_item = proto_tree_add_item(fiveco_data_tree, hf_fiveco_regread, |
492 | 0 | tvb, tcp_data_offset + i, 0, ENC_NA); |
493 | 0 | proto_item_append_text(fiveco_data_item, " %s", aRegisters[ucRegAdd].name); |
494 | 0 | } |
495 | 0 | else |
496 | 0 | { |
497 | 0 | fiveco_data_item = proto_tree_add_item(fiveco_data_tree, hf_fiveco_regreadunknown, |
498 | 0 | tvb, tcp_data_offset + i, 0, ENC_NA); |
499 | 0 | } |
500 | 0 | proto_item_append_text(fiveco_data_item, " (0x%.2X)", ucRegAdd); |
501 | 0 | } |
502 | 0 | break; |
503 | 0 | case WRITE_REGISTER: |
504 | 0 | case WRITE_REGISTER_QUIET: |
505 | | // List register asked to write with data to fill in until an unknown one is found |
506 | 0 | for (i = tcp_data_offset; i < tcp_data_offset + header_data_length;) |
507 | 0 | { |
508 | 0 | ucRegAdd = tvb_get_uint8(tvb, i++); |
509 | | // If register address is known & found |
510 | 0 | if ((ucRegAdd < array_length(aRegisters)) && |
511 | 0 | (aRegisters[ucRegAdd].unValue == ucRegAdd)) |
512 | 0 | { |
513 | 0 | ucRegSize = aRegisters[ucRegAdd].unSize; |
514 | | // If a display function is defined, call it |
515 | 0 | if (aRegisters[ucRegAdd].pFct != NULL) |
516 | 0 | { |
517 | 0 | proto_tree_add_item(fiveco_data_tree, aRegisters[ucRegAdd].nsWsHeaderID, |
518 | 0 | tvb, i, ucRegSize, ENC_NA); |
519 | 0 | i += ucRegSize; |
520 | 0 | } |
521 | | // else if register type is string, display it as string |
522 | 0 | else if (aRegisters[ucRegAdd].ft == FT_STRING) |
523 | 0 | { |
524 | 0 | fiveco_data_item = proto_tree_add_item(fiveco_data_tree, |
525 | 0 | aRegisters[ucRegAdd].nsWsHeaderID, |
526 | 0 | tvb, i, ucRegSize, |
527 | 0 | ENC_NA); |
528 | 0 | proto_item_append_text(fiveco_data_item, ": %s", tvb_format_text(pinfo->pool, tvb, i, ucRegSize)); |
529 | 0 | i += ucRegSize; |
530 | 0 | } |
531 | | // else display raw data in hex |
532 | 0 | else |
533 | 0 | { |
534 | 0 | fiveco_data_item = proto_tree_add_item(fiveco_data_tree, hf_fiveco_regread, |
535 | 0 | tvb, i, ucRegSize, ENC_NA); |
536 | 0 | proto_item_append_text(fiveco_data_item, " %s (Add: 0x%.2X, Size: %d bytes): ", |
537 | 0 | aRegisters[ucRegAdd].name, ucRegAdd, ucRegSize); |
538 | 0 | for (j = 0; j < ucRegSize; j++) |
539 | 0 | { |
540 | 0 | proto_item_append_text(fiveco_data_item, "0x%.2X ", tvb_get_uint8(tvb, i++)); |
541 | 0 | } |
542 | 0 | } |
543 | 0 | } |
544 | | // Else tell user that data cannot be interpreted |
545 | 0 | else |
546 | 0 | { |
547 | 0 | fiveco_data_item = proto_tree_add_item(fiveco_data_tree, hf_fiveco_regreaduk, |
548 | 0 | tvb, i, tcp_data_offset + header_data_length - i, ENC_NA); |
549 | 0 | proto_item_append_text(fiveco_data_item, " (Interpretation depends on product type)"); |
550 | 0 | break; |
551 | 0 | } |
552 | 0 | } |
553 | 0 | break; |
554 | 0 | case EASY_IP_ADDRESS_CONFIG: |
555 | 0 | proto_tree_add_item(fiveco_data_tree, hf_fiveco_EasyIPMAC, tvb, tcp_data_offset + 0, 6, ENC_NA); |
556 | 0 | proto_tree_add_item(fiveco_data_tree, hf_fiveco_EasyIPIP, tvb, tcp_data_offset + 6, 4, ENC_BIG_ENDIAN); |
557 | 0 | proto_tree_add_item(fiveco_data_tree, hf_fiveco_EasyIPSM, tvb, tcp_data_offset + 10, 4, ENC_BIG_ENDIAN); |
558 | 0 | break; |
559 | 0 | case I2C_READ_ANSWER: |
560 | 0 | case I2C_WRITE_ANSWER: |
561 | 0 | case I2C_READ_WRITE_ACK_ANSWER: |
562 | 0 | if (pRequestVal) |
563 | 0 | { |
564 | 0 | if (pRequestVal->isReplied != 0) |
565 | 0 | { |
566 | 0 | proto_item_append_text(fiveco_data_item, |
567 | 0 | " WARNING : Answer already found ! Maybe packets ID not incremented."); |
568 | 0 | } |
569 | 0 | else |
570 | 0 | { |
571 | 0 | i = tcp_data_offset; // Answer index |
572 | 0 | y = 0; // Request index |
573 | 0 | while ((y < pRequestVal->usParaLen) && (i < tcp_data_offset + header_data_length)) |
574 | 0 | { |
575 | | // I2C address in first byte of request |
576 | 0 | ucAdd = tvb_get_uint8(pRequestTvb, y++); |
577 | | // Read number of bytes to write |
578 | 0 | ucBytesToWrite = tvb_get_uint8(pRequestTvb, y); |
579 | | // Skip number of bytes to write and those bytes |
580 | 0 | y += 1 + ucBytesToWrite; |
581 | | // Read number of bytes to read |
582 | 0 | ucBytesToRead = tvb_get_uint8(pRequestTvb, y++); |
583 | 0 | if (ucBytesToRead > 0) |
584 | 0 | { |
585 | 0 | fiveco_data_item = proto_tree_add_item(fiveco_data_tree, hf_fiveco_i2canswer, |
586 | 0 | tvb, i, ucBytesToRead, ENC_NA); |
587 | 0 | proto_item_append_text(fiveco_data_item, |
588 | 0 | " from address %d (%d bytes written) : ", |
589 | 0 | ucAdd, ucBytesToWrite); |
590 | 0 | for (j = 0; j < ucBytesToRead; j++) |
591 | 0 | { |
592 | 0 | proto_item_append_text(fiveco_data_item, "0x%.2X ", |
593 | 0 | tvb_get_uint8(tvb, i++)); |
594 | 0 | } |
595 | 0 | if (header_type == 0x08) |
596 | 0 | proto_tree_add_item(fiveco_data_tree, hf_fiveco_i2cack, tvb, i++, 1, ENC_BIG_ENDIAN); |
597 | 0 | } |
598 | 0 | else if (header_type == I2C_READ_WRITE_ACK_ANSWER) |
599 | 0 | { |
600 | | // if it's an answer to a write but with ack, display it |
601 | 0 | fiveco_data_item = proto_tree_add_item(fiveco_data_tree, |
602 | 0 | hf_fiveco_i2cwriteanswer, tvb, i, |
603 | 0 | ucBytesToRead, ENC_NA); |
604 | 0 | proto_item_append_text(fiveco_data_item, " to address %d (%d bytes written)", |
605 | 0 | ucAdd, ucBytesToWrite); |
606 | 0 | proto_tree_add_item(fiveco_data_tree, hf_fiveco_i2cack, tvb, i++, 1, ENC_BIG_ENDIAN); |
607 | 0 | } |
608 | 0 | } |
609 | 0 | } |
610 | 0 | break; |
611 | 0 | } |
612 | 0 | else { |
613 | 0 | proto_item_append_text(fiveco_data_item, " (Interpretation depends on product type)"); |
614 | 0 | } |
615 | 0 | break; |
616 | 0 | case READ_REGISTER_ANSWER: |
617 | 0 | if (pRequestVal) |
618 | 0 | { |
619 | 0 | if (pRequestVal->isReplied != 0) |
620 | 0 | { |
621 | 0 | proto_item_append_text(fiveco_data_item, |
622 | 0 | " WARNING : Answer already found ! Maybe packets ID not incremented."); |
623 | 0 | } |
624 | 0 | else |
625 | 0 | { |
626 | 0 | i = tcp_data_offset; // Answer index |
627 | 0 | y = 0; // Request index |
628 | | // For each request stored in the last read request of the conversation |
629 | 0 | while ((y < pRequestVal->usParaLen) && (i < tcp_data_offset + header_data_length)) |
630 | 0 | { |
631 | | // Register address in first byte of request |
632 | 0 | ucRegAdd = tvb_get_uint8(pRequestTvb, y++); |
633 | | // If register address is known & found in answer |
634 | 0 | if ((ucRegAdd < array_length(aRegisters)) && |
635 | 0 | (aRegisters[ucRegAdd].unValue == ucRegAdd) && |
636 | 0 | (ucRegAdd == tvb_get_uint8(tvb, i++))) |
637 | 0 | { |
638 | | // Retrieve register size and display it with address |
639 | 0 | ucRegSize = aRegisters[ucRegAdd].unSize; |
640 | | // If a display function is defined, call it |
641 | 0 | if (aRegisters[ucRegAdd].pFct != NULL) |
642 | 0 | { |
643 | 0 | proto_tree_add_item(fiveco_data_tree, aRegisters[ucRegAdd].nsWsHeaderID, |
644 | 0 | tvb, i, ucRegSize, ENC_NA); |
645 | 0 | i += ucRegSize; |
646 | 0 | } |
647 | | // else if register type is string, display it as string |
648 | 0 | else if (aRegisters[ucRegAdd].ft == FT_STRING) |
649 | 0 | { |
650 | 0 | fiveco_data_item = proto_tree_add_item(fiveco_data_tree, |
651 | 0 | aRegisters[ucRegAdd].nsWsHeaderID, |
652 | 0 | tvb, i, ucRegSize, |
653 | 0 | ENC_NA); |
654 | 0 | proto_item_append_text(fiveco_data_item, ": %s", tvb_format_text(pinfo->pool, tvb, i, ucRegSize)); |
655 | 0 | i += ucRegSize; |
656 | 0 | } |
657 | | // else display raw data in hex |
658 | 0 | else |
659 | 0 | { |
660 | 0 | fiveco_data_item = proto_tree_add_item(fiveco_data_tree, |
661 | 0 | hf_fiveco_regread, tvb, i, ucRegSize, ENC_NA); |
662 | 0 | proto_item_append_text(fiveco_data_item, |
663 | 0 | " %s (Add: 0x%.2X, Size: %d bytes): ", |
664 | 0 | aRegisters[ucRegAdd].name, ucRegAdd, ucRegSize); |
665 | 0 | for (j = 0; j < ucRegSize; j++) |
666 | 0 | { |
667 | 0 | proto_item_append_text(fiveco_data_item, |
668 | 0 | "0x%.2X ", tvb_get_uint8(tvb, i++)); |
669 | 0 | } |
670 | 0 | } |
671 | 0 | } |
672 | | // Else tell user that data cannot be interpreted |
673 | 0 | else |
674 | 0 | { |
675 | 0 | fiveco_data_item = proto_tree_add_item(fiveco_data_tree, |
676 | 0 | hf_fiveco_regreaduk, tvb, i, |
677 | 0 | tcp_data_offset + header_data_length - i, |
678 | 0 | ENC_NA); |
679 | 0 | proto_item_append_text(fiveco_data_item, |
680 | 0 | " (Interpretation depends on product type)"); |
681 | 0 | break; |
682 | 0 | } |
683 | 0 | } |
684 | 0 | } |
685 | 0 | } |
686 | 0 | break; |
687 | 0 | case FLASH_AREA_LOAD: |
688 | 0 | unOffset = tvb_get_uint24(tvb, tcp_data_offset, ENC_BIG_ENDIAN); |
689 | 0 | unSize = tvb_get_uint24(tvb, tcp_data_offset + 3, ENC_BIG_ENDIAN); |
690 | 0 | proto_item_append_text(fiveco_data_item, |
691 | 0 | " (%d bytes to load into flash at offset %d)", unSize, unOffset); |
692 | 0 | break; |
693 | 0 | case FLASH_AREA_ANSWER: |
694 | 0 | if ( header_data_length > 1 ) { |
695 | 0 | proto_item_append_text(fiveco_data_item, " (%s)", tvb_format_text(pinfo->pool, tvb, tcp_data_offset, header_data_length - 1)); |
696 | 0 | } |
697 | 0 | break; |
698 | | |
699 | 0 | case WRITE_REGISTER_ANSWER: |
700 | 0 | case FLASH_AREA_ERASE: |
701 | 0 | case EASY_IP_ADDRESS_CONFIG_ANSWER: |
702 | 0 | proto_item_append_text(fiveco_data_item, " (ERROR: No data should be present with that packet type !!)"); |
703 | 0 | break; |
704 | | |
705 | 0 | default: |
706 | 0 | proto_item_append_text(fiveco_data_item, " (Interpretation depends on product type)"); |
707 | 0 | break; |
708 | 0 | } |
709 | 0 | } |
710 | | |
711 | | // Checksum validation and sub tree |
712 | 0 | proto_tree_add_checksum(fiveco_tree, tvb, tcp_data_offset + header_data_length, hf_fiveco_cks, -1, NULL, NULL, |
713 | 0 | checksum_cal, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY); |
714 | |
|
715 | 0 | tcp_data_offset += header_data_length + 2 ; /* jump to next packet if exists */ |
716 | 0 | } /*while (tcp_data_offset < tcp_data_length) */ |
717 | | |
718 | 0 | return tvb_captured_length(tvb); |
719 | 0 | } |
720 | | |
721 | | /*****************************************************************************/ |
722 | | /* This function returns the calculated checksum (IP based) */ |
723 | | /*****************************************************************************/ |
724 | | static uint16_t checksum_fiveco(tvbuff_t *byte_tab, uint16_t start_offset, uint16_t size) |
725 | 0 | { |
726 | 0 | uint32_t Sum = 0; |
727 | 0 | uint8_t AddHighByte = 1; |
728 | 0 | uint32_t ChecksumCalculated; |
729 | 0 | uint16_t i; |
730 | 0 | uint8_t temp; |
731 | |
|
732 | 0 | for (i = 0; i < size; i++) |
733 | 0 | { |
734 | 0 | tvb_memcpy(byte_tab, (uint8_t *)&temp, start_offset + i, 1); |
735 | 0 | if (AddHighByte) |
736 | 0 | { |
737 | 0 | Sum += (temp << 8) ^ 0xFF00; |
738 | 0 | AddHighByte = 0; |
739 | 0 | } |
740 | 0 | else |
741 | 0 | { |
742 | 0 | Sum += (temp) ^ 0x00FF; |
743 | 0 | AddHighByte = 1; |
744 | 0 | } |
745 | 0 | } |
746 | |
|
747 | 0 | if (AddHighByte == 0) |
748 | 0 | Sum += 0xFF; |
749 | |
|
750 | 0 | ChecksumCalculated = ((Sum >> 16) & 0xFFFF) + (Sum & 0xFFFF); |
751 | 0 | ChecksumCalculated = ((ChecksumCalculated >> 16) & 0xFFFF) + (ChecksumCalculated & 0xFFFF); |
752 | 0 | return (uint16_t)ChecksumCalculated; |
753 | 0 | } |
754 | | |
755 | | /*****************************************************************************/ |
756 | | /* Compute an unique hash value */ |
757 | | /*****************************************************************************/ |
758 | | static unsigned fiveco_hash(const void *v) |
759 | 0 | { |
760 | 0 | const FCOSConvRequestKey *key = (const FCOSConvRequestKey *)v; |
761 | 0 | unsigned val; |
762 | |
|
763 | 0 | val = key->conversation + (((key->usExpCmd) & 0xFFFF) << 16) + |
764 | 0 | (key->unInternalID & 0xFFFFFFFF) + ((key->unInternalID >>32) & 0xFFFFFFFF); |
765 | |
|
766 | 0 | return val; |
767 | 0 | } |
768 | | |
769 | | /*****************************************************************************/ |
770 | | /* Check hash equal */ |
771 | | /*****************************************************************************/ |
772 | | static int fiveco_hash_equal(const void *v, const void *w) |
773 | 0 | { |
774 | 0 | const FCOSConvRequestKey *v1 = (const FCOSConvRequestKey *)v; |
775 | 0 | const FCOSConvRequestKey *v2 = (const FCOSConvRequestKey *)w; |
776 | |
|
777 | 0 | if (v1->conversation == v2->conversation && |
778 | 0 | v1->usExpCmd == v2->usExpCmd && |
779 | 0 | v1->unInternalID == v2->unInternalID) |
780 | 0 | { |
781 | 0 | return 1; |
782 | 0 | } |
783 | 0 | return 0; |
784 | 0 | } |
785 | | |
786 | | /*****************************************************************************/ |
787 | | /* Register the protocol with Wireshark. |
788 | | * |
789 | | * This format is required because a script is used to build the C function that |
790 | | * calls all the protocol registration. |
791 | | */ |
792 | | /*****************************************************************************/ |
793 | | void proto_register_FiveCoLegacy(void) |
794 | 14 | { |
795 | | /* Setup list of header fields (based on static table and specific table) */ |
796 | 14 | static hf_register_info hf[array_length(hf_base) + array_length(aRegisters)]; |
797 | 322 | for (uint32_t i = 0; i < array_length(hf_base); i++) { |
798 | 308 | hf[i] = hf_base[i]; |
799 | 308 | } |
800 | 322 | for (uint32_t i = 0; i < array_length(aRegisters); i++) { |
801 | 308 | if (aRegisters[i].pFct != NULL){ |
802 | 84 | hf_register_info hfx = { &(aRegisters[i].nsWsHeaderID),{aRegisters[i].name, aRegisters[i].abbrev, aRegisters[i].ft, BASE_CUSTOM, aRegisters[i].pFct, 0x0, NULL, HFILL}}; |
803 | 84 | hf[array_length(hf_base) + i] = hfx; |
804 | 224 | } else { |
805 | 224 | hf_register_info hfx = { &(aRegisters[i].nsWsHeaderID),{aRegisters[i].name, aRegisters[i].abbrev, FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL}}; |
806 | 224 | hf[array_length(hf_base) + i] = hfx; |
807 | 224 | } |
808 | 308 | } |
809 | | |
810 | | /* Setup protocol subtree array */ |
811 | 14 | static int *ett[] = { |
812 | 14 | &ett_fiveco_header, |
813 | 14 | &ett_fiveco_data, |
814 | 14 | &ett_fiveco, |
815 | 14 | &ett_fiveco_checksum}; |
816 | | |
817 | | /* Register the protocol name and description */ |
818 | 14 | proto_FiveCoLegacy = proto_register_protocol("FiveCo's Legacy Register Access Protocol", |
819 | 14 | PSNAME, "5co_legacy"); |
820 | | |
821 | | /* Required function calls to register the header fields and subtrees */ |
822 | 14 | proto_register_field_array(proto_FiveCoLegacy, hf, array_length(hf)); |
823 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
824 | | |
825 | | /* Register the dissector */ |
826 | 14 | FiveCoLegacy_handle = register_dissector("5co_legacy", dissect_FiveCoLegacy, |
827 | 14 | proto_FiveCoLegacy); |
828 | | |
829 | 14 | FiveCo_requests_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), fiveco_hash, fiveco_hash_equal); |
830 | | |
831 | | /* Set preference callback to NULL since it is not used */ |
832 | 14 | prefs_register_protocol(proto_FiveCoLegacy, NULL); |
833 | 14 | } |
834 | | |
835 | | /* If this dissector uses sub-dissector registration add a registration routine. |
836 | | * This exact format is required because a script is used to find these |
837 | | * routines and create the code that calls these routines. |
838 | | * |
839 | | * Simpler form of proto_reg_handoff_FiveCoLegacy which can be used if there are |
840 | | * no prefs-dependent registration function calls. */ |
841 | | void proto_reg_handoff_FiveCoLegacy(void) |
842 | 14 | { |
843 | 14 | static bool initialized = false; |
844 | | |
845 | 14 | if (!initialized) |
846 | 14 | { |
847 | 14 | dissector_add_uint("tcp.port", FIVECO_PORT1, FiveCoLegacy_handle); |
848 | 14 | dissector_add_uint("tcp.port", FIVECO_PORT2, FiveCoLegacy_handle); |
849 | 14 | dissector_add_uint("udp.port", FIVECO_UDP_PORT1, FiveCoLegacy_handle); |
850 | 14 | initialized = true; |
851 | 14 | } |
852 | 14 | } |
853 | | |
854 | | /*****************************************************************************/ |
855 | | /* Registers decoding functions */ |
856 | | /*****************************************************************************/ |
857 | | static void |
858 | | dispType( char *result, uint32_t type) |
859 | 0 | { |
860 | 0 | int nValueH = (type>>16) & 0xFFFF; |
861 | 0 | int nValueL = (type & 0xFFFF); |
862 | 0 | snprintf( result, ITEM_LABEL_LENGTH, "%d.%d (%.4X.%.4X)", nValueH, nValueL, nValueH, nValueL); |
863 | 0 | } |
864 | | |
865 | | static void |
866 | | dispVersion( char *result, uint32_t version) |
867 | 0 | { |
868 | 0 | if ((version & 0xFF000000) == 0) |
869 | 0 | { |
870 | 0 | int nValueH = (version>>16) & 0xFFFF; |
871 | 0 | int nValueL = (version & 0xFFFF); |
872 | 0 | snprintf( result, ITEM_LABEL_LENGTH, "FW: %d.%d", nValueH, nValueL); |
873 | 0 | } |
874 | 0 | else |
875 | 0 | { |
876 | 0 | int nHWHigh = (version>>24) & 0xFF; |
877 | 0 | int nHWLow = (version>>16) & 0xFF; |
878 | 0 | int nFWHigh = (version>>8) & 0xFF; |
879 | 0 | int nFWLow = (version>>8) & 0xFF; |
880 | 0 | snprintf( result, ITEM_LABEL_LENGTH, "HW: %d.%d / FW: %d.%d", nHWHigh, nHWLow, nFWHigh, nFWLow); |
881 | 0 | } |
882 | 0 | } |
883 | | |
884 | | static void dispMAC( char *result, uint64_t mac) |
885 | 0 | { |
886 | 0 | uint8_t *pData = (uint8_t*)(&mac); |
887 | |
|
888 | 0 | snprintf( result, ITEM_LABEL_LENGTH, "%.2X-%.2X-%.2X-%.2X-%.2X-%.2X", pData[5], pData[4], pData[3], pData[2], |
889 | 0 | pData[1], pData[0]); |
890 | 0 | } |
891 | | |
892 | | static void dispIP( char *result, uint32_t ip) |
893 | 0 | { |
894 | 0 | uint8_t *pData = (uint8_t*)(&ip); |
895 | |
|
896 | 0 | snprintf( result, ITEM_LABEL_LENGTH, "%d.%d.%d.%d", pData[3], pData[2], pData[1], pData[0]); |
897 | 0 | } |
898 | | |
899 | | static void dispMask( char *result, uint32_t mask) |
900 | 0 | { |
901 | 0 | uint8_t *pData = (uint8_t*)(&mask); |
902 | |
|
903 | 0 | snprintf( result, ITEM_LABEL_LENGTH, "%d.%d.%d.%d", pData[3], pData[2], pData[1], pData[0]); |
904 | 0 | } |
905 | | |
906 | | static void dispTimeout( char *result, uint32_t timeout) |
907 | 0 | { |
908 | 0 | if (timeout != 0) |
909 | 0 | snprintf( result, ITEM_LABEL_LENGTH, "%d seconds", timeout); |
910 | 0 | else |
911 | 0 | snprintf( result, ITEM_LABEL_LENGTH, "Disabled"); |
912 | 0 | } |
913 | | |
914 | | /* |
915 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
916 | | * |
917 | | * Local variables: |
918 | | * c-basic-offset: 4 |
919 | | * tab-width: 8 |
920 | | * indent-tabs-mode: nil |
921 | | * End: |
922 | | * |
923 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
924 | | * :indentSize=4:tabSize=8:noTabs=true: |
925 | | */ |