2 * Routines for FiveCo's Register Access Protocol dissector
3 * Copyright 2021, Antoine Gardiol <antoine.gardiol@fiveco.ch>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
13 * This protocol allows access to FiveCo's Ethernet products registers with FRAP
14 * protocol. Product list can be found under https://www.fiveco.ch/5-stars-products.
15 * Protocol description can be found (by example) in FMod-I2C485ECMOT DB 48/10 manual that can
16 * be dowloaded from https://www.fiveco.ch/sites/default/files/2021-09/FiveCo_MotorCtrl_UserManual_1_9.pdf.
17 * Note that this protocol is a question-answer protocol. It's header is composed of:
18 * - 1 byte for destination address (useless over IP)
19 * - 1 byte for source address (useless over IP)
20 * - x bytes for data length of parameters (high bits set tells that a supplementary byte is used)
21 * The header is followed by n bytes of data (including checksum)
24 //#define DEBUG_5co-rap
27 #define WS_LOG_DOMAIN "5co-rap"
29 #include <epan/packet.h>
30 #include <epan/to_str.h>
31 #include <epan/unit_strings.h>
32 #include <wsutil/utf8_entities.h>
33 #include <wsutil/array.h>
34 #include "packet-tcp.h"
37 void proto_reg_handoff_FiveCoRAP(void);
38 void proto_register_FiveCoRAP(void);
40 /****************************************************************************/
41 /* Definition declaration */
42 /****************************************************************************/
44 // Protocol header length and frame minimum length
45 #define FIVECO_RAP_HEADER_LENGTH 3
46 #define FIVECO_RAP_MIN_LENGTH FIVECO_RAP_HEADER_LENGTH + 2 // Checksum is 2 bytes
47 #define MAX_LENGTH_BYTES 4 // Max number of bytes for data length
48 #define MAX_SUB_DEVICES 10
50 #define PROTO_TAG_FIVECO "5co-rap"
52 /* Global sample ports preferences */
53 #define FIVECO_TCP_PORT1 8030 /* TCP port of the FiveCo protocol (N.B. unassigned by IANA) */
54 #define FIVECO_UDP_PORT1 7030 /* UDP port of the FiveCo protocol (N.B. assigned to "op-probe" by IANA) */
56 /* 16 bits type known available functions */
60 READ_REGISTER_ANSWER
= 0x20,
61 WRITE_REGISTER
= 0x40,
62 SUBDEVICE_ROUTING
= 0xC0,
63 SUBDEVICE_ROUTING_ANSWER
= 0xD0,
64 EXT_REGISTER_ACCESS_ERR
= 0xE0,
69 EXT_EOF_MULTI_PACKETS
,
70 EXT_EOF_MULTI_PACKETS_END
,
71 EXT_EASY_IP_ADDRESS_CHANGE
74 /* Forward references to functions */
76 checksum_fiveco(tvbuff_t
* byte_tab
, uint16_t start_offset
, uint16_t size
);
77 static int fiveco_hash_equal(const void *v
, const void *w
);
79 /* Register decoding functions prototypes */
80 static void disp_type( char *result
, uint32_t type
);
81 static void disp_version( char *result
, uint32_t type
);
82 static void disp_voltage( char *result
, uint32_t type
);
83 static void disp_mac( char *result
, uint64_t type
);
84 static void disp_ip( char *result
, uint32_t type
);
85 static void disp_mask( char *result
, uint32_t type
);
86 static void disp_timeout( char *result
, uint32_t type
);
88 /* Initialize the protocol and registered fields */
89 static int proto_FiveCoRAP
; /* Wireshark ID of the FiveCo protocol */
91 /* The following hf_* variables are used to hold the Wireshark IDs of */
92 /* our header fields; they are filled out when we call */
93 /* proto_register_field_array() in proto_register_fiveco() */
94 static int hf_fiveco_source_addr
;
95 static int hf_fiveco_dest_addr
;
96 static int hf_fiveco_data
;
97 static int hf_fiveco_regread
;
98 static int hf_fiveco_regread_answer
;
99 static int hf_fiveco_regwrite
;
100 static int hf_fiveco_regcall
;
101 static int hf_fiveco_routing
;
102 static int hf_fiveco_routing_answer
;
103 static int hf_fiveco_routing_interface
;
104 static int hf_fiveco_routing_timeout
;
105 static int hf_fiveco_routing_size
;
106 static int hf_fiveco_ext_regerror
;
107 static int hf_fiveco_ext_frameid
;
108 static int hf_fiveco_ext_eof
;
109 static int hf_fiveco_ext_frameerror
;
110 static int hf_fiveco_ext_easyip
;
111 static int hf_fiveco_ext_easyip_version
;
112 static int hf_fiveco_ext_easyip_interface
;
113 static int hf_fiveco_ext_easyip_mac
;
114 static int hf_fiveco_ext_easyip_ip
;
115 static int hf_fiveco_ext_easyip_mask
;
116 static int hf_fiveco_ext_unsupported
;
117 static int hf_fiveco_cks
;
119 /* These are the ids of the subtrees that we may be creating */
120 /* for the header fields. */
121 static int ett_fiveco
[MAX_SUB_DEVICES
];
122 static int ett_fiveco_data
[MAX_SUB_DEVICES
];
123 static int ett_fiveco_easyip
[MAX_SUB_DEVICES
];
124 static int ett_fiveco_sub
[MAX_SUB_DEVICES
];
125 static int ett_fiveco_sub_details
[MAX_SUB_DEVICES
];
127 /* Conversation request key structure */
130 uint32_t conversation
;
133 /* Conversation device type structure */
136 uint32_t device_type
[MAX_SUB_DEVICES
];
137 uint32_t device_version
[MAX_SUB_DEVICES
];
140 /* Conversation hash table (conversation-id -> FCOSConvDevices*) */
141 /* TODO: could just have FCOSConvDevices* as conversation data type? */
142 static GHashTable
*fiveco_types_models_hash
;
144 enum FCOERegistersType
{
149 /* Register definition structure (used to detect known registers when it is possible) */
152 const uint32_t reg_size
; // Register size (in bytes)
153 const uint32_t reg_type
; // Register type (register, function)
154 const char *name
; // Register name
155 const char *abbrev
; // Abbreviation base for header fill
156 const enum ftenum ft
; // Field type
157 const int32_t base
; // Base display type
158 const unsigned encoding
; // Field encoding
159 int hf_id_w
; // Wireshark ID for header fill in write mode
160 int hf_id_r_a
; // Wireshark ID for header fill in read answer mode
161 const void *cf_func
; // Conversion function
164 /* Known (common on every product) registers */
165 static FCOSRegisterDef registers_def
[] = {
166 /*0x00*/ { 4, REGISTER
, "Type/Model", "5co_rap.RegTypeModel", FT_UINT32
, BASE_CUSTOM
, ENC_LITTLE_ENDIAN
, -1, -1, CF_FUNC(disp_type
)},
167 /*0x01*/ { 4, REGISTER
, "Version", "5co_rap.RegVersion", FT_UINT32
, BASE_CUSTOM
, ENC_LITTLE_ENDIAN
, -1, -1, CF_FUNC(disp_version
)},
168 /*0x02*/ { 0, FUNCTION
, "Reset device", "5co_rap.RegReset", FT_NONE
, BASE_NONE
, ENC_NA
, -1, -1, NULL
},
169 /*0x03*/ { 0, FUNCTION
, "Save user parameters", "5co_rap.RegSave", FT_NONE
, BASE_NONE
, ENC_NA
, -1, -1, NULL
},
170 /*0x04*/ { 0, FUNCTION
, "Restore user parameters", "5co_rap.RegRestore", FT_NONE
, BASE_NONE
, ENC_NA
, -1, -1, NULL
},
171 /*0x05*/ { 0, FUNCTION
, "Restore factory parameters", "5co_rap.RegRestoreFact", FT_NONE
, BASE_NONE
, ENC_NA
, -1, -1, NULL
},
172 /*0x06*/ { 0, FUNCTION
, "Save factory parameters", "5co_rap.SaveFact", FT_NONE
, BASE_NONE
, ENC_NA
, -1, -1, NULL
},
173 /*0x07*/ { 4, REGISTER
, "Voltage", "5co_rap.Voltage", FT_UINT32
, BASE_CUSTOM
, ENC_LITTLE_ENDIAN
, -1, -1, CF_FUNC(disp_voltage
)},
174 /*0x08*/ { 4, REGISTER
, "Warnings", "5co_rap.Warnings", FT_UINT32
, BASE_HEX
, ENC_LITTLE_ENDIAN
, -1, -1, NULL
},
175 /*0x09*/ { 8, REGISTER
, "Time Read", "5co_rap.TimeR", FT_UINT64
, BASE_HEX
, ENC_NA
, -1, -1, NULL
},
176 /*0x0A*/ { 8, REGISTER
, "Time Write", "5co_rap.TimeW", FT_UINT64
, BASE_HEX
, ENC_NA
, -1, -1, NULL
},
177 /*0x0B*/ { 4, REGISTER
, "Number of power up", "5co_rap.NbPowerUp", FT_UINT32
, BASE_DEC
, ENC_LITTLE_ENDIAN
, -1, -1, NULL
},
178 /*0x0C*/ { 4, REGISTER
, "Service time (seconds)", "5co_rap.ServiceTime", FT_UINT32
, BASE_DEC
, ENC_LITTLE_ENDIAN
, -1, -1, NULL
},
179 /*0x0D*/ { 0, REGISTER
, "Unknown", "5co_rap.RegUnknown0D", FT_NONE
, BASE_NONE
, ENC_NA
, -1, -1, NULL
},
180 /*0x0E*/ { 8, REGISTER
, "CPU usage", "5co_rap.CPUUsage", FT_UINT64
, BASE_HEX
, ENC_NA
, -1, -1, NULL
},
181 /*0x0F*/ { 0, REGISTER
, "Unknown", "5co_rap.RegUnknown0F", FT_NONE
, BASE_NONE
, ENC_NA
, -1, -1, NULL
},
182 /*0x10*/ { 4, REGISTER
, "Communication options", "5co_rap.RegComOption", FT_UINT32
, BASE_HEX
, ENC_LITTLE_ENDIAN
, -1, -1, NULL
},
183 /*0x11*/ { 6, REGISTER
, "Ethernet MAC Address", "5co_rap.RegMAC", FT_UINT48
, BASE_CUSTOM
, ENC_NA
, -1, -1, CF_FUNC(disp_mac
)},
184 /*0x12*/ { 4, REGISTER
, "IP Address / Com ID", "5co_rap.RegIPAdd", FT_UINT32
, BASE_CUSTOM
, ENC_NA
, -1, -1, CF_FUNC(disp_ip
)},
185 /*0x13*/ { 4, REGISTER
, "IP Mask", "5co_rap.RegIPMask", FT_UINT32
, BASE_CUSTOM
, ENC_NA
, -1, -1, CF_FUNC(disp_mask
)},
186 /*0x14*/ { 1, REGISTER
, "TCP Timeout", "5co_rap.RegTCPTimeout", FT_UINT8
, BASE_CUSTOM
, ENC_LITTLE_ENDIAN
, -1, -1, CF_FUNC(disp_timeout
)},
187 /*0x15*/ { 16, REGISTER
, "Module name", "5co_rap.RegName", FT_STRING
, BASE_NONE
, ENC_NA
, -1, -1, NULL
},
188 /*0x16*/ { 0, REGISTER
, "Unknown", "5co_rap.RegUnknown15", FT_NONE
, BASE_NONE
, ENC_NA
, -1, -1, NULL
},
189 /*0x17*/ { 0, REGISTER
, "Unknown", "5co_rap.RegUnknown16", FT_NONE
, BASE_NONE
, ENC_NA
, -1, -1, NULL
},
190 /*0x18*/ {16, REGISTER
, "FW upgrade flash data 0", "5co_rap.FwUpgFlashData0", FT_BYTES
, SEP_SPACE
, ENC_NA
, -1, -1, NULL
},
191 /*0x19*/ {16, REGISTER
, "FW upgrade flash data 1", "5co_rap.FwUpgFlashData1", FT_BYTES
, SEP_SPACE
, ENC_NA
, -1, -1, NULL
},
192 /*0x1A*/ {16, REGISTER
, "FW upgrade flash data 2", "5co_rap.FwUpgFlashData2", FT_BYTES
, SEP_SPACE
, ENC_NA
, -1, -1, NULL
},
193 /*0x1B*/ {16, REGISTER
, "FW upgrade flash data 3", "5co_rap.FwUpgFlashData3", FT_BYTES
, SEP_SPACE
, ENC_NA
, -1, -1, NULL
},
194 /*0x1C*/ { 6, REGISTER
, "FW upgrade flash pointer", "5co_rap.FwUpgFlashPointer", FT_BYTES
, SEP_SPACE
, ENC_NA
, -1, -1, NULL
},
195 /*0x1D*/ { 0, FUNCTION
, "FW upgrade execute", "5co_rap.FwForceExecute", FT_NONE
, BASE_NONE
, ENC_NA
, -1, -1, NULL
}
198 /* List of static header fields */
199 static hf_register_info hf_base
[] = {
200 {&hf_fiveco_source_addr
, {"Source address", "5co_rap.src_addr", FT_UINT8
, BASE_DEC_HEX
, NULL
, 0x0, "FRAP source address", HFILL
}},
201 {&hf_fiveco_dest_addr
, {"Destination address", "5co_rap.dest_addr", FT_UINT8
, BASE_DEC_HEX
, NULL
, 0x0, "FRAP destination address", HFILL
}},
202 {&hf_fiveco_data
, {"Data", "5co_rap.data", FT_NONE
, BASE_NONE
, NULL
, 0x0, "Data (parameters)", HFILL
}},
203 {&hf_fiveco_regread
, {"Read register", "5co_rap.regread", FT_NONE
, BASE_NONE
, NULL
, 0x0, "Read register at index", HFILL
}},
204 {&hf_fiveco_regread_answer
, {"Read answer register", "5co_rap.regreadans", FT_NONE
, BASE_NONE
, NULL
, 0x0, "Answer to a read register", HFILL
}},
205 {&hf_fiveco_regwrite
, {"Write register", "5co_rap.regwrite", FT_NONE
, BASE_NONE
, NULL
, 0x0, "Write register at index", HFILL
}},
206 {&hf_fiveco_regcall
, {"Call function", "5co_rap.regcall", FT_NONE
, BASE_NONE
, NULL
, 0x0, "Call function at index", HFILL
}},
207 {&hf_fiveco_routing
, {"Routing to subdevice", "5co_rap.routing", FT_NONE
, BASE_NONE
, NULL
, 0x0, "Frame to be routed to a sub device", HFILL
}},
208 {&hf_fiveco_routing_answer
, {"Answer from subdevice", "5co_rap.routinganswer", FT_NONE
, BASE_NONE
, NULL
, 0x0, "Answer from a subdevice", HFILL
}},
209 {&hf_fiveco_routing_interface
, {"Interface", "5co_rap.routinginterface", FT_NONE
, BASE_NONE
, NULL
, 0x0, "Device routing interface for sub device", HFILL
}},
210 {&hf_fiveco_routing_timeout
, {"Timeout", "5co_rap.routingtimeout", FT_UINT8
, BASE_HEX
, NULL
, 0x0, "Answer timeout from the sub device", HFILL
}},
211 {&hf_fiveco_routing_size
, {"Size of frame to route", "5co_rap.routingsize", FT_NONE
, BASE_NONE
, NULL
, 0x0, "Size of frame to be routed to a sub device", HFILL
}},
212 {&hf_fiveco_ext_regerror
, {"Register access error", "5co_rap.regerror", FT_NONE
, BASE_NONE
, NULL
, 0x0, "Error while accessing a register", HFILL
}},
213 {&hf_fiveco_ext_frameid
, {"Frame ID", "5co_rap.frameid", FT_NONE
, BASE_NONE
, NULL
, 0x0, "ID of the frame", HFILL
}},
214 {&hf_fiveco_ext_eof
, {"End of frame", "5co_rap.eof", FT_NONE
, BASE_NONE
, NULL
, 0x0, "End of the frame", HFILL
}},
215 {&hf_fiveco_cks
, {"Checksum", "5co_rap.checksum", FT_UINT8
, BASE_HEX_DEC
, NULL
, 0x0, "Checksum of the frame", HFILL
}},
216 {&hf_fiveco_ext_frameerror
, {"Frame error", "5co_rap.frameerror", FT_NONE
, BASE_NONE
, NULL
, 0x0, "Frame error occurred", HFILL
}},
217 {&hf_fiveco_ext_easyip
, {"Easy IP configuration", "5co_rap.easyip", FT_NONE
, BASE_NONE
, NULL
, 0x0, "Change IP config easily by broadcast", HFILL
}},
218 {&hf_fiveco_ext_easyip_version
, {"Extension version", "5co_rap.easyipversion", FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
219 {&hf_fiveco_ext_easyip_interface
, {"Destination FRAP interface", "5co_rap.easyipinterface", FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
220 {&hf_fiveco_ext_easyip_mac
, {"Destination MAC address", "5co_rap.easyipmac", FT_ETHER
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
221 {&hf_fiveco_ext_easyip_ip
, {"New IP address", "5co_rap.easyipip", FT_IPv4
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
222 {&hf_fiveco_ext_easyip_mask
, {"New subnet mask", "5co_rap.easyipmask", FT_IPv4
, BASE_NETMASK
, NULL
, 0x0, NULL
, HFILL
}},
223 {&hf_fiveco_ext_unsupported
, {"Unsupported function", "5co_rap.frameunsupported", FT_NONE
, BASE_NONE
, NULL
, 0x0, "Function ignored by this dissector", HFILL
}},
226 /*****************************************************************************/
227 /* Code to actually compute a data size */
228 /* This function compute a datasize with from a packet. */
229 /* Data size in this protocol is one or more bytes based. Seven lower bits */
230 /* are used for size and if higher bit is set, the next byte is also used and*/
231 /* so on until a byte with higher bit is not set. */
232 /*****************************************************************************/
233 /* XXX - Replace with equivalent tvb_get_varint(..., ENC_VARINT_PROTOBUF)? */
235 get_data_size(tvbuff_t
*tvb
, uint32_t first_index
, uint32_t *p_header_len
) {
238 uint32_t data_size
= 0;
239 uint32_t max_len
= MAX_LENGTH_BYTES
+ *p_header_len
;
240 uint32_t size_len
= 0; // Length of size area minus 1
242 for (; *p_header_len
< max_len
; (*p_header_len
)++) {
243 size8
= tvb_get_uint8(tvb
, first_index
+ *p_header_len
);
245 data_size
|= (size8
& 0x7F) << (7 * size_len
);
248 data_size
|= size8
<< (7 * size_len
);
256 /*****************************************************************************/
257 /* Code to dissect data from the packets */
258 /* Recursive function !! */
259 /*****************************************************************************/
261 // NOLINTNEXTLINE(misc-no-recursion)
262 dissect_frame(tvbuff_t
*tvb
, packet_info
* pinfo
, proto_tree
* fiveco_frame_tree
, FCOSConvDevices
*types_models_p
,
263 uint32_t frame_index
, uint32_t frame_size
, uint32_t *sub_index_p
)
265 uint8_t checksum_cal
, checksum_rx
;
271 proto_item
*fiveco_item
= NULL
;
272 proto_item
*fiveco_header_item
= NULL
;
273 proto_item
* fiveco_data_item
= NULL
;
274 proto_item
* fiveco_routing_item
= NULL
;
275 proto_tree
*fiveco_tree
= NULL
;
276 proto_tree
* fiveco_data_tree
= NULL
;
277 proto_tree
* fiveco_easyip_tree
= NULL
;
278 proto_tree
* fiveco_routing_details_tree
= NULL
;
279 proto_tree
* fiveco_routing_tree
= NULL
;
285 uint8_t routing_interface
;
286 uint8_t routing_timeout
;
287 uint32_t routing_size
;
288 uint32_t routing_size_pos
;
289 uint32_t routing_header_len
;
291 /* Retrieve header info */
292 dest_addr
= tvb_get_uint8(tvb
, frame_index
+ 0);
293 source_addr
= tvb_get_uint8(tvb
, frame_index
+ 1);
295 data_size
= get_data_size(tvb
, frame_index
, &header_len
);
296 /* If data size is null or greater than captured data, abort */
299 if (data_size
> frame_size
- frame_index
- header_len
) {
303 /* Compute checksum of the packet and read one received */
304 checksum_cal
= checksum_fiveco(tvb
, frame_index
, header_len
+ data_size
- 1);
305 checksum_rx
= tvb_get_uint8(tvb
, frame_index
+ header_len
+ data_size
- 1);
307 /* Add text to info column */
308 /* If the offset != 0 (not first fiveco frame in tcp packet) add a comma in info column */
309 if (frame_index
!= 0)
311 col_append_fstr(pinfo
->cinfo
, COL_INFO
, ", %d " UTF8_RIGHTWARDS_ARROW
" %d Len=%d", source_addr
, dest_addr
, data_size
);
315 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "%d " UTF8_RIGHTWARDS_ARROW
" %d Len=%d", source_addr
, dest_addr
, data_size
);
318 if (checksum_rx
!= checksum_cal
)
320 col_append_str(pinfo
->cinfo
, COL_INFO
, " [BAD CHECKSUM !!]");
323 /* Add FiveCo protocol in tree (after TCP or UDP entry) */
324 fiveco_item
= proto_tree_add_item(fiveco_frame_tree
, proto_FiveCoRAP
, tvb
, frame_index
+ 0,
325 header_len
+ data_size
, ENC_NA
); /* Add a new entry inside tree display */
326 proto_item_append_text(fiveco_item
, ", Src Addr: %d, Dst Addr: %d, Len: %d", source_addr
, dest_addr
, data_size
);
328 /* Add fiveco Protocol tree and sub trees for Header, Data and Checksum */
329 fiveco_tree
= proto_item_add_subtree(fiveco_item
, ett_fiveco
[*sub_index_p
]); // FiveCo prot tree
330 fiveco_header_item
= proto_tree_add_item(fiveco_tree
, hf_fiveco_dest_addr
,
331 tvb
, frame_index
+ 0, 1, ENC_NA
);
333 // Add destination address in the tree plus information about the device
336 proto_item_append_text(fiveco_header_item
, " Broadcast message");
338 if (types_models_p
->device_type
[*sub_index_p
] != 0)
340 proto_item_append_text(fiveco_header_item
, ", Detected device: %d.%d",
341 (types_models_p
->device_type
[*sub_index_p
]>>16),
342 (types_models_p
->device_type
[*sub_index_p
] & 0xFFFF));
344 if (types_models_p
->device_version
[*sub_index_p
] != 0)
346 if (((types_models_p
->device_version
[*sub_index_p
] & 0xFF000000) == 0) &&
347 ((types_models_p
->device_version
[*sub_index_p
] & 0x0000FF00) == 0))
348 proto_item_append_text(fiveco_header_item
, ", Version: %d.%d",
349 (types_models_p
->device_version
[*sub_index_p
]>>16),
350 (types_models_p
->device_version
[*sub_index_p
] & 0xFFFF));
352 proto_item_append_text(fiveco_header_item
, ", Version: HW=%d.%d FW=%d.%d",
353 (types_models_p
->device_version
[*sub_index_p
]>>24) & 0xFF,
354 (types_models_p
->device_version
[*sub_index_p
]>>16) & 0xFF,
355 (types_models_p
->device_version
[*sub_index_p
]>>8) & 0xFF,
356 (types_models_p
->device_version
[*sub_index_p
] & 0xFF));
358 /* Add source address in the tree */
359 proto_tree_add_item(fiveco_tree
, hf_fiveco_source_addr
, tvb
, frame_index
+ 1, 1, ENC_NA
);
360 /* Add data length in the tree */
361 fiveco_header_item
= proto_tree_add_item(fiveco_tree
, hf_fiveco_data
, tvb
, frame_index
+ header_len
,
363 proto_item_append_text(fiveco_header_item
, " (%d bytes)", data_size
);
364 /* Add subtree for dissected data */
365 fiveco_data_tree
= proto_item_add_subtree(fiveco_header_item
, ett_fiveco_data
[*sub_index_p
]);
367 /* Start data dissection */
368 frame_index
+= header_len
; /* put offset on start of data (parameters) */
370 for (i
= frame_index
; i
< frame_index
+ data_size
;)
372 /* Get type of next data */
373 data_type
= tvb_get_uint8(tvb
, i
);
375 /* Handle data type (mask since only 3 high bits are relevant) */
376 switch (data_type
& 0xE0)
378 /* Handle read register command (data type = 0) */
380 /* 5 lower bits give the register length between 0 and 31 */
381 reg_size
= data_type
& 0x1F;
382 /* Next byte give the register address */
383 reg_addr
= tvb_get_uint8(tvb
, i
+ 1);
384 /* Add read register entry in the tree including its name (if known) and size */
385 fiveco_data_item
= proto_tree_add_item(fiveco_data_tree
, hf_fiveco_regread
, tvb
,
387 if ((reg_addr
< array_length(registers_def
)) && (registers_def
[reg_addr
].reg_size
== reg_size
))
389 proto_item_append_text(fiveco_data_item
, " 0x%.2X (Name: %s, Size: %d)",
390 reg_addr
, registers_def
[reg_addr
].name
, reg_size
);
394 proto_item_append_text(fiveco_data_item
, " 0x%.2X (Name: Unknown, Size: %d)",
400 /* Handle an answer to a read register command (data type = 32) */
401 case READ_REGISTER_ANSWER
:
402 /* 5 lower bits give the register length between 0 and 31 */
403 reg_size
= data_type
& 0x1F;
404 /* Next byte give the register address */
405 reg_addr
= tvb_get_uint8(tvb
, i
+ 1);
407 /* If type register is found, remember it into types_models_p list */
408 if (reg_addr
== 0x00)
410 types_models_p
->device_type
[*sub_index_p
] = tvb_get_uint32(tvb
, i
+ 2, ENC_LITTLE_ENDIAN
);
412 else if (reg_addr
== 0x01)
414 types_models_p
->device_version
[*sub_index_p
] = tvb_get_uint32(tvb
, i
+ 2, ENC_LITTLE_ENDIAN
);
417 /* If register is in the registers_def array */
418 if ((reg_addr
< array_length(registers_def
)) && (registers_def
[reg_addr
].reg_size
== reg_size
))
420 /* If display type is not defined, display raw data manually */
421 if (registers_def
[reg_addr
].ft
== FT_NONE
)
423 fiveco_data_item
= proto_tree_add_item(fiveco_data_tree
,
424 registers_def
[reg_addr
].hf_id_r_a
,
425 tvb
, i
+2, reg_size
, registers_def
[reg_addr
].encoding
);
426 proto_item_append_text(fiveco_data_item
, ": ");
427 for (j
= 0; j
< reg_size
; j
++)
429 proto_item_append_text(fiveco_data_item
, "%.2X ",
430 tvb_get_uint8(tvb
, i
+ 2 + j
));
433 /* else display based on predefined type */
435 proto_tree_add_item(fiveco_data_tree
, registers_def
[reg_addr
].hf_id_r_a
,
436 tvb
, i
+2, reg_size
, registers_def
[reg_addr
].encoding
);
439 /* else display raw data in hex manually */
442 fiveco_data_item
= proto_tree_add_item(fiveco_data_tree
, hf_fiveco_regread_answer
, tvb
,
443 i
, 2 + reg_size
, ENC_NA
);
444 proto_item_append_text(fiveco_data_item
, " 0x%.2X (Name: Unknown, Size: %d): ",
446 for (j
= 0; j
< reg_size
; j
++)
448 proto_item_append_text(fiveco_data_item
, "%.2X ",
449 tvb_get_uint8(tvb
, i
+ 2 + j
));
454 /* Handle a write register command */
456 /* 5 lower bits give the register length between 0 and 31 */
457 reg_size
= data_type
& 0x1F;
458 /* Next byte give the register address */
459 reg_addr
= tvb_get_uint8(tvb
, i
+ 1);
461 /* If register is in the registers_def array */
462 if ((reg_addr
< array_length(registers_def
)) && (registers_def
[reg_addr
].reg_size
== reg_size
))
464 /* If display type is not defined, display raw data manually (nothing for functions) */
465 if (registers_def
[reg_addr
].ft
== FT_NONE
)
467 fiveco_data_item
= proto_tree_add_item(fiveco_data_tree
,
468 registers_def
[reg_addr
].hf_id_w
,
469 tvb
, i
+2, reg_size
, registers_def
[reg_addr
].encoding
);
470 /* Add data for register write */
471 if (registers_def
[reg_addr
].reg_type
== REGISTER
) {
472 proto_item_append_text(fiveco_data_item
, ": ");
473 for (j
= 0; j
< reg_size
; j
++)
475 proto_item_append_text(fiveco_data_item
, "0x%.2X ",
476 tvb_get_uint8(tvb
, i
+ 2 + j
));
480 /* else display based on predefined type */
482 proto_tree_add_item(fiveco_data_tree
, registers_def
[reg_addr
].hf_id_w
,
483 tvb
, i
+2, reg_size
, registers_def
[reg_addr
].encoding
);
486 /* else display raw data in hex manually */
489 /* If size is > 0 then it is a write with data */
491 fiveco_data_item
= proto_tree_add_item(fiveco_data_tree
, hf_fiveco_regwrite
, tvb
,
492 i
, 2 + reg_size
, ENC_NA
);
493 proto_item_append_text(fiveco_data_item
, " 0x%.2X (Name: Unknown, Size: %d): ",
495 for (j
= 0; j
< reg_size
; j
++)
497 proto_item_append_text(fiveco_data_item
, "%.2X ",
498 tvb_get_uint8(tvb
, i
+ 2 + j
));
501 /* else it is a function call */
503 fiveco_data_item
= proto_tree_add_item(fiveco_data_tree
, hf_fiveco_regcall
, tvb
,
504 i
, 2 + reg_size
, ENC_NA
);
505 proto_item_append_text(fiveco_data_item
, " 0x%.2X (Name: Unknown, Size: %d)",
512 case EXT_REGISTER_ACCESS_ERR
:
513 /* Handle extensions data type */
516 case EXT_REGISTER_ACCESS_ERR
:
517 reg_addr
= tvb_get_uint8(tvb
, i
+ 1);
518 fiveco_data_item
= proto_tree_add_item(fiveco_data_tree
, hf_fiveco_ext_regerror
, tvb
,
520 proto_item_append_text(fiveco_data_item
, ": Index 0x%.2X", reg_addr
);
525 case EXT_FRAME_ID_ANSWER
:
526 fiveco_data_item
= proto_tree_add_item(fiveco_data_tree
, hf_fiveco_ext_frameid
, tvb
,
528 proto_item_append_text(fiveco_data_item
, ": %d",
529 tvb_get_uint8(tvb
, i
+ 1));
534 proto_tree_add_item(fiveco_data_tree
, hf_fiveco_ext_eof
, tvb
,
536 proto_tree_add_checksum(fiveco_tree
, tvb
, i
+ 1, hf_fiveco_cks
, -1, NULL
, NULL
,
537 checksum_cal
, ENC_LITTLE_ENDIAN
, PROTO_CHECKSUM_VERIFY
);
541 case EXT_FRAME_ERROR
:
542 proto_tree_add_item(fiveco_data_tree
, hf_fiveco_ext_frameerror
, tvb
,
546 case EXT_EOF_MULTI_PACKETS
:
547 case EXT_EOF_MULTI_PACKETS_END
:
548 proto_tree_add_item(fiveco_data_tree
, hf_fiveco_ext_unsupported
, tvb
,
552 case EXT_EASY_IP_ADDRESS_CHANGE
:
553 fiveco_data_item
= proto_tree_add_item(fiveco_data_tree
, hf_fiveco_ext_easyip
, tvb
,
556 sz_mac
= tvb_ether_to_str(pinfo
->pool
, tvb
, i
+3);
557 sz_new_ip
= tvb_ip_to_str(pinfo
->pool
, tvb
, i
+9);
558 proto_item_append_text(fiveco_data_item
, ": New IP: %s for %s", sz_new_ip
, sz_mac
);
559 fiveco_easyip_tree
= proto_item_add_subtree(fiveco_data_item
, ett_fiveco_easyip
[*sub_index_p
]);
560 proto_tree_add_item(fiveco_easyip_tree
, hf_fiveco_ext_easyip_version
, tvb
,
562 proto_tree_add_item(fiveco_easyip_tree
, hf_fiveco_ext_easyip_interface
, tvb
,
564 proto_tree_add_item(fiveco_easyip_tree
, hf_fiveco_ext_easyip_mac
, tvb
,
566 proto_tree_add_item(fiveco_easyip_tree
, hf_fiveco_ext_easyip_ip
, tvb
,
568 proto_tree_add_item(fiveco_easyip_tree
, hf_fiveco_ext_easyip_mask
, tvb
,
574 /* If type is still unknown, stop handling the packet */
575 i
= frame_index
+ data_size
;
581 /* Handle data type with 4 high bits relevant */
582 switch (data_type
& 0xF0)
584 case SUBDEVICE_ROUTING
:
585 case SUBDEVICE_ROUTING_ANSWER
:
586 /* Handle routed frames by recursive call of this function */
587 routing_interface
= (data_type
& 0x0F);
588 if ((data_type
& 0xF0) == SUBDEVICE_ROUTING
)
590 routing_size_pos
= 2;
591 routing_header_len
= 2;
592 routing_size
= get_data_size(tvb
, i
, &routing_header_len
);
593 routing_timeout
= tvb_get_uint8(tvb
, i
+ 1);
594 fiveco_routing_item
= proto_tree_add_item(fiveco_data_tree
, hf_fiveco_routing
, tvb
,
595 i
, routing_header_len
+ routing_size
, ENC_NA
);
596 proto_item_append_text(fiveco_routing_item
, " (Interface: %d, Timeout: %d, Frame size: %d)",
597 routing_interface
, routing_timeout
, routing_size
);
601 routing_size_pos
= 1;
602 routing_header_len
= 1;
603 routing_size
= get_data_size(tvb
, i
, &routing_header_len
);
604 fiveco_routing_item
= proto_tree_add_item(fiveco_data_tree
, hf_fiveco_routing_answer
, tvb
,
605 i
, routing_header_len
+ routing_size
, ENC_NA
);
606 proto_item_append_text(fiveco_routing_item
, " (Interface: %d, Frame size: %d)",
607 routing_interface
, routing_size
);
610 /* Recursive call !! */
611 if (*sub_index_p
< (MAX_SUB_DEVICES
-1)) {
613 fiveco_routing_details_tree
= proto_item_add_subtree(fiveco_routing_item
, ett_fiveco_sub_details
[*sub_index_p
]);
614 fiveco_data_item
= proto_tree_add_item(fiveco_routing_details_tree
, hf_fiveco_routing_interface
, tvb
, i
, 1, ENC_NA
);
615 proto_item_append_text(fiveco_data_item
, " %d", routing_interface
);
616 if ((data_type
& 0xF0) == SUBDEVICE_ROUTING
) {
617 proto_tree_add_item(fiveco_routing_details_tree
, hf_fiveco_routing_timeout
, tvb
, i
+ 1, 1, ENC_LITTLE_ENDIAN
);
619 fiveco_data_item
= proto_tree_add_item(fiveco_routing_details_tree
, hf_fiveco_routing_size
, tvb
,
620 i
+ routing_size_pos
, routing_header_len
- routing_size_pos
, ENC_NA
);
621 proto_item_append_text(fiveco_data_item
, " %d", routing_size
);
622 i
+= routing_header_len
;
623 fiveco_routing_tree
= proto_item_add_subtree(fiveco_routing_item
, ett_fiveco_sub
[*sub_index_p
]);
624 dissect_frame(tvb
, pinfo
, fiveco_routing_tree
, types_models_p
, i
, frame_size
, sub_index_p
);
626 proto_item_append_text(fiveco_routing_item
,
627 " Sub frame cannot be displayed because max number of subdevices that can be dissected is exceeded !");
628 i
+= routing_header_len
;
633 /* If type is still unknown, stop handling the packet */
634 i
= frame_index
+ data_size
;
644 /*****************************************************************************/
645 /* Code to actually dissect the packets */
646 /* Callback function for reassembled packet */
647 /*****************************************************************************/
649 dissect_FiveCoRAP(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
652 uint32_t tcp_data_offset
= 0;
653 uint32_t tcp_data_length
= 0;
654 uint32_t sub_devices_count
= 0;
655 conversation_t
*conversation
;
656 FCOSConvKey conversation_key
, *new_conversation_key_p
;
657 FCOSConvDevices
*types_models_p
;
659 uint32_t types_models_count
= 0;
662 /* Load protocol payload length (including checksum) */
663 tcp_data_length
= tvb_captured_length(tvb
);
664 if (tcp_data_length
< FIVECO_RAP_MIN_LENGTH
) /* Check checksum presence */
667 /* Display fiveco in protocol column */
668 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, PROTO_TAG_FIVECO
);
669 /* Clear out stuff in the info column */
670 col_clear(pinfo
->cinfo
, COL_INFO
);
672 /* Look for all future TCP conversations between the
673 * requesting server and the FiveCo device using the
674 * same src & dest addr and ports.
676 conversation
= find_or_create_conversation(pinfo
);
677 conversation_key
.conversation
= conversation
->conv_index
;
679 /* Retrieve current types/model structure of the conversation */
680 types_models_p
= (FCOSConvDevices
*)g_hash_table_lookup(fiveco_types_models_hash
, &conversation_key
);
684 ws_message("Adding conversation %d in hash table", conversation_key
.conversation
);
687 new_conversation_key_p
= wmem_new(wmem_file_scope(), FCOSConvKey
);
688 *new_conversation_key_p
= conversation_key
;
690 types_models_p
= wmem_new(wmem_file_scope(), FCOSConvDevices
);
691 for (i
= 0; i
< MAX_SUB_DEVICES
; i
++)
693 types_models_p
->device_type
[i
] = 0; /* Set device type of all (sub-)devices to unknown */
694 types_models_p
->device_version
[i
] = 0; /* Set device version of all (sub-)devices to unknown */
696 g_hash_table_insert(fiveco_types_models_hash
, new_conversation_key_p
, types_models_p
);
701 for (i
= 0; i
< MAX_SUB_DEVICES
; i
++)
703 if (types_models_p
->device_type
[i
] != 0)
704 types_models_count
++;
706 ws_message("Found %d types/models in conversation %d from hash table",
707 types_models_count
, conversation_key
.conversation
);
711 /* Loop because several fiveco packets can be present in one TCP packet */
712 while (tcp_data_offset
< tcp_data_length
) {
714 /* Handle data and jump to next packet if exists */
715 tcp_data_offset
= dissect_frame(tvb
, pinfo
, tree
, types_models_p
,
716 tcp_data_offset
, tcp_data_length
, &sub_devices_count
);
717 if (tcp_data_offset
== 0) /* If no FRAP frame is found, abort */
720 } /*while (tcp_data_offset < tcp_data_length) */
722 return tvb_captured_length(tvb
);
725 /*****************************************************************************/
726 /* This function returns the calculated checksum (IP based) */
727 /*****************************************************************************/
728 static uint8_t checksum_fiveco(tvbuff_t
*byte_tab
, uint16_t start_offset
, uint16_t size
)
733 for (i
= 0; i
< size
; i
++)
735 sum
+= tvb_get_uint8(byte_tab
, start_offset
+ i
);
738 return (uint8_t)(sum
& 0xFF);
741 /*****************************************************************************/
742 /* Compute an unique hash value */
743 /*****************************************************************************/
744 static unsigned fiveco_hash(const void *v
)
746 const FCOSConvKey
*key
= (const FCOSConvKey
*)v
;
747 return key
->conversation
;
750 /*****************************************************************************/
751 /* Check hash equal */
752 /*****************************************************************************/
753 static int fiveco_hash_equal(const void *v
, const void *w
)
755 const FCOSConvKey
*v1
= (const FCOSConvKey
*)v
;
756 const FCOSConvKey
*v2
= (const FCOSConvKey
*)w
;
758 return (v1
->conversation
== v2
->conversation
);
761 /*****************************************************************************/
762 /* Protocol initialization function */
763 /*****************************************************************************/
764 static void fiveco_protocol_init(void)
766 if (fiveco_types_models_hash
)
767 g_hash_table_destroy(fiveco_types_models_hash
);
768 fiveco_types_models_hash
= g_hash_table_new(fiveco_hash
, fiveco_hash_equal
);
771 /*****************************************************************************/
772 /* Register the protocol with Wireshark.
774 * This format is required because a script is used to build the C function that
775 * calls all the protocol registration.
777 /*****************************************************************************/
778 void proto_register_FiveCoRAP(void)
782 /* Following variables are used to allocate string buffer to store
783 name and abbreviations strings for the hf table */
784 wmem_strbuf_t
* hf_name_read_answer_buf
= NULL
;
785 wmem_strbuf_t
* hf_name_write_buf
= NULL
;
786 wmem_strbuf_t
* hf_abbrev_read_answer_buf
= NULL
;
787 wmem_strbuf_t
* hf_abbrev_write_buf
= NULL
;
789 /* Setup list of header fields (based on static table and specific table) */
790 static hf_register_info hf
[(array_length(hf_base
)) + (2*array_length(registers_def
))];
791 for (i
= 0; i
< array_length(hf_base
); i
++) {
795 for (i
= 0; i
< array_length(registers_def
); i
++) {
797 /* Create string buffer for current row in registers_def */
798 hf_name_read_answer_buf
= wmem_strbuf_new(wmem_epan_scope(), "");
799 hf_name_write_buf
= wmem_strbuf_new(wmem_epan_scope(), "");
800 hf_abbrev_read_answer_buf
= wmem_strbuf_new(wmem_epan_scope(), "");
801 hf_abbrev_write_buf
= wmem_strbuf_new(wmem_epan_scope(), "");
803 /* Construct read answer and write hf abbreviations for the current row in registers_def */
804 wmem_strbuf_append_printf(hf_abbrev_read_answer_buf
, "%s.readanswer", registers_def
[i
].abbrev
);
805 wmem_strbuf_append_printf(hf_abbrev_write_buf
, "%s.write", registers_def
[i
].abbrev
);
807 /* Construct read answer and write hf name for the current row in registers_def */
808 if (registers_def
[i
].reg_type
== REGISTER
) {
809 wmem_strbuf_append_printf(hf_name_read_answer_buf
, "Read answer register 0x%.2X (Name: %s, Size: %d)", i
, registers_def
[i
].name
, registers_def
[i
].reg_size
);
810 wmem_strbuf_append_printf(hf_name_write_buf
, "Write register 0x%.2X (Name: %s, Size: %d)", i
, registers_def
[i
].name
, registers_def
[i
].reg_size
);
813 wmem_strbuf_append_printf(hf_name_read_answer_buf
, "Invalid read answer register 0x%.2X (Name: %s): A function cannot have a read answer", i
, registers_def
[i
].name
);
814 wmem_strbuf_append_printf(hf_name_write_buf
, "Call function 0x%.2X (Name: %s)", i
, registers_def
[i
].name
);
817 if (registers_def
[i
].cf_func
!= NULL
) {
818 hf_register_info hfxw
= { &(registers_def
[i
].hf_id_w
),{wmem_strbuf_get_str(hf_name_write_buf
), wmem_strbuf_get_str(hf_abbrev_write_buf
), registers_def
[i
].ft
, registers_def
[i
].base
, registers_def
[i
].cf_func
, 0x0, NULL
, HFILL
} };
819 hf
[array_length(hf_base
) + i
] = hfxw
;
820 hf_register_info hfxra
= { &(registers_def
[i
].hf_id_r_a
),{wmem_strbuf_get_str(hf_name_read_answer_buf
), wmem_strbuf_get_str(hf_abbrev_read_answer_buf
), registers_def
[i
].ft
, registers_def
[i
].base
, registers_def
[i
].cf_func
, 0x0, NULL
, HFILL
} };
821 hf
[array_length(hf_base
) + array_length(registers_def
) + i
] = hfxra
;
824 hf_register_info hfxw
= { &(registers_def
[i
].hf_id_w
),{wmem_strbuf_get_str(hf_name_write_buf
), wmem_strbuf_get_str(hf_abbrev_write_buf
), registers_def
[i
].ft
, registers_def
[i
].base
, NULL
, 0x0, NULL
, HFILL
} };
825 hf
[array_length(hf_base
) + i
] = hfxw
;
826 hf_register_info hfxra
= { &(registers_def
[i
].hf_id_r_a
),{wmem_strbuf_get_str(hf_name_read_answer_buf
), wmem_strbuf_get_str(hf_abbrev_read_answer_buf
), registers_def
[i
].ft
, registers_def
[i
].base
, NULL
, 0x0, NULL
, HFILL
} };
827 hf
[array_length(hf_base
) + array_length(registers_def
) + i
] = hfxra
;
831 /* Setup protocol subtree array for each possible nested devices */
832 static int *ett
[5 * MAX_SUB_DEVICES
];
833 for (i
= 0; i
< MAX_SUB_DEVICES
; i
++)
835 ett
[5*i
+ 0] = &ett_fiveco
[i
];
836 ett
[5*i
+ 1] = &ett_fiveco_data
[i
];
837 ett
[5*i
+ 2] = &ett_fiveco_easyip
[i
];
838 ett
[5*i
+ 3] = &ett_fiveco_sub
[i
];
839 ett
[5*i
+ 4] = &ett_fiveco_sub_details
[i
];
842 /* Register the dissector */
843 /* Register the protocol name and description */
844 proto_FiveCoRAP
= proto_register_protocol("FiveCo RAP Register Access Protocol",
845 PROTO_TAG_FIVECO
, "5co_rap");
847 /* Required function calls to register the header fields and subtrees */
848 proto_register_field_array(proto_FiveCoRAP
, hf
, array_length(hf
));
849 proto_register_subtree_array(ett
, array_length(ett
));
851 /* Register hash init function
852 * Protocol hash is used to follow types/models of devices in a conversation.
854 register_init_routine(&fiveco_protocol_init
);
856 /* Set preference callback to NULL since it is not used */
857 prefs_register_protocol(proto_FiveCoRAP
, NULL
);
860 /* If this dissector uses sub-dissector registration add a registration routine.
861 * This exact format is required because a script is used to find these
862 * routines and create the code that calls these routines.
864 * Simpler form of proto_reg_handoff_FiveCoRAP which can be used if there are
865 * no prefs-dependent registration function calls. */
866 void proto_reg_handoff_FiveCoRAP(void)
868 static bool initialized
= false;
869 static dissector_handle_t FiveCoRAP_handle
;
873 /* Use create_dissector_handle() to indicate that
874 * dissect_FiveCoRAP() returns the number of bytes it dissected (or 0
875 * if it thinks the packet does not belong to PROTONAME).
877 FiveCoRAP_handle
= create_dissector_handle(dissect_FiveCoRAP
,
879 dissector_add_uint("tcp.port", FIVECO_TCP_PORT1
, FiveCoRAP_handle
);
880 dissector_add_uint("udp.port", FIVECO_UDP_PORT1
, FiveCoRAP_handle
);
885 /*****************************************************************************/
886 /* Registers decoding function */
887 /*****************************************************************************/
889 disp_type( char *result
, uint32_t type
)
891 unsigned nValueH
= (type
>>16) & 0xFFFF;
892 unsigned nValueL
= (type
& 0xFFFF);
893 snprintf( result
, ITEM_LABEL_LENGTH
, "%u.%u (%.4X.%.4X)", nValueH
, nValueL
, nValueH
, nValueL
);
897 disp_version( char *result
, uint32_t version
)
899 if ((version
& 0xFF000000) == 0)
901 unsigned nValueH
= (version
>>16) & 0xFFFF;
902 unsigned nValueL
= (version
& 0xFFFF);
903 snprintf( result
, ITEM_LABEL_LENGTH
, "FW: %u.%u", nValueH
, nValueL
);
907 unsigned nHWHigh
= (version
>>24) & 0xFF;
908 unsigned nHWLow
= (version
>>16) & 0xFF;
909 unsigned nFWHigh
= (version
>>8) & 0xFF;
910 unsigned nFWLow
= version
& 0xFF;
911 snprintf( result
, ITEM_LABEL_LENGTH
, "HW: %u.%u / FW: %u.%u", nHWHigh
, nHWLow
, nFWHigh
, nFWLow
);
915 static void disp_voltage(char *result
, uint32_t voltage
)
917 unsigned nValueH
= (voltage
>>16) & 0xFFFF;
918 unsigned nValueL
= (voltage
& 0xFFFF);
919 snprintf( result
, ITEM_LABEL_LENGTH
, "%u.%u V", nValueH
, nValueL
);
922 static void disp_mac( char *result
, uint64_t mac
)
924 uint8_t *pData
= (uint8_t*)(&mac
);
926 snprintf( result
, ITEM_LABEL_LENGTH
, "%.2X-%.2X-%.2X-%.2X-%.2X-%.2X", pData
[5], pData
[4], pData
[3], pData
[2],
930 static void disp_ip( char *result
, uint32_t ip
)
932 uint8_t *pData
= (uint8_t*)(&ip
);
934 snprintf( result
, ITEM_LABEL_LENGTH
, "%u.%u.%u.%u", pData
[3], pData
[2], pData
[1], pData
[0]);
937 static void disp_mask( char *result
, uint32_t mask
)
939 uint8_t *pData
= (uint8_t*)(&mask
);
941 snprintf( result
, ITEM_LABEL_LENGTH
, "%u.%u.%u.%u", pData
[3], pData
[2], pData
[1], pData
[0]);
944 static void disp_timeout( char *result
, uint32_t timeout
)
947 snprintf( result
, ITEM_LABEL_LENGTH
, "%u%s",
948 timeout
, unit_name_string_get_value(timeout
, &units_second_seconds
));
950 snprintf( result
, ITEM_LABEL_LENGTH
, "Disabled");
954 * Editor modelines - https://www.wireshark.org/tools/modelines.html
959 * indent-tabs-mode: nil
962 * vi: set shiftwidth=4 tabstop=8 expandtab:
963 * :indentSize=4:tabSize=8:noTabs=true: