2 * Routines for Scylla RPC dissection
3 * Copyright 2020 ScyllaDB, Piotr Sarna <sarna@scylladb.com>
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 * ScyllaDB RPC protocol is used for inter-node communication
14 * in the ScyllaDB database - reading/sending data, exchanging
15 * cluster information through gossip, updating schemas, etc.
17 * Protocol references:
18 * https://github.com/scylladb/seastar/blob/master/doc/rpc.md
19 * https://github.com/scylladb/scylla/blob/master/message/messaging_service.hh
25 #include <epan/expert.h>
26 #include <epan/packet.h>
27 #include <epan/prefs.h>
28 #include "packet-tcp.h"
30 void proto_reg_handoff_scylla(void);
31 void proto_register_scylla(void);
33 static dissector_handle_t scylla_handle
;
35 #define SCYLLA_PORT 0 /* Not IANA registered, 7000 is the expected value */
37 #define SCYLLA_HEADER_SIZE 28
38 #define SCYLLA_HEADER_VERB_OFFSET 8
39 #define SCYLLA_HEADER_MSG_ID_OFFSET 16
40 #define SCYLLA_HEADER_LEN_OFFSET 24
42 #define SCYLLA_RESPONSE_SIZE 12
43 #define SCYLLA_RESPONSE_MSG_ID_OFFSET 0
44 #define SCYLLA_RESPONSE_LEN_OFFSET 8
46 #define SCYLLA_NEGOTIATION_SIZE 12
47 #define SCYLLA_NEGOTIATION_LEN_OFFSET 8
49 static int proto_scylla
;
51 static int hf_scylla_request
;
52 static int hf_scylla_request_response_frame
;
53 static int hf_scylla_timeout
;
54 static int hf_scylla_verb
;
55 static int hf_scylla_msg_id
;
56 static int hf_scylla_len
;
57 static int hf_scylla_response
;
58 static int hf_scylla_response_size
;
59 static int hf_scylla_response_request_frame
;
60 static int hf_scylla_negotiation_magic
;
61 static int hf_scylla_negotiation_size
;
62 static int hf_scylla_feature_number
;
63 static int hf_scylla_feature_len
;
64 static int hf_scylla_feature_data
;
65 static int hf_scylla_connection_id
;
66 static int hf_scylla_isolation_cookie
;
67 static int hf_scylla_streaming_len
;
68 static int hf_scylla_payload
; // TODO: dissect everything, so that generic "payload" is not needed
71 static int hf_scylla_mut_size1
;
72 static int hf_scylla_mut_size2
;
73 static int hf_scylla_mut_table_id
;
74 static int hf_scylla_mut_schema_id
;
75 static int hf_scylla_mut_len_pkeys
;
76 static int hf_scylla_mut_num_pkeys
;
77 static int hf_scylla_mut_len_pkey
;
78 static int hf_scylla_mut_pkey
;
81 static int hf_scylla_read_data_timeout
;
82 static int hf_scylla_read_data_table_id
;
83 static int hf_scylla_read_data_schema_version
;
85 static int ett_scylla
;
86 static int ett_scylla_header
;
87 static int ett_scylla_response
;
88 static int ett_scylla_negotiation
;
89 static int ett_scylla_negotiation_features
;
90 static int ett_sclla_streaming
;
91 static int ett_scylla_mut
;
92 static int ett_scylla_mut_pkey
;
93 static int ett_scylla_read_data
;
95 static bool scylla_desegment
= true;
97 static expert_field ei_scylla_response_missing
;
104 READ_MUTATION_DATA
= 4,
107 GOSSIP_DIGEST_SYN
= 6,
108 GOSSIP_DIGEST_ACK
= 7,
109 GOSSIP_DIGEST_ACK2
= 8,
111 GOSSIP_SHUTDOWN
= 10,
112 // end of gossip verb
113 DEFINITIONS_UPDATE
= 11,
115 REPLICATION_FINISHED
= 13,
116 MIGRATION_REQUEST
= 14,
118 PREPARE_MESSAGE
= 15,
119 PREPARE_DONE_MESSAGE
= 16,
120 STREAM_MUTATION
= 17,
121 STREAM_MUTATION_DONE
= 18,
122 COMPLETE_MESSAGE
= 19,
123 // end of streaming verbs
124 REPAIR_CHECKSUM_RANGE
= 20,
125 GET_SCHEMA_VERSION
= 21,
127 COUNTER_MUTATION
= 23,
128 MUTATION_FAILED
= 24,
129 STREAM_MUTATION_FRAGMENTS
= 25,
130 REPAIR_ROW_LEVEL_START
= 26,
131 REPAIR_ROW_LEVEL_STOP
= 27,
132 REPAIR_GET_FULL_ROW_HASHES
= 28,
133 REPAIR_GET_COMBINED_ROW_HASH
= 29,
134 REPAIR_GET_SYNC_BOUNDARY
= 30,
135 REPAIR_GET_ROW_DIFF
= 31,
136 REPAIR_PUT_ROW_DIFF
= 32,
137 REPAIR_GET_ESTIMATED_PARTITIONS
= 33,
138 REPAIR_SET_ESTIMATED_PARTITIONS
= 34,
139 REPAIR_GET_DIFF_ALGORITHMS
= 35,
140 REPAIR_GET_ROW_DIFF_WITH_RPC_STREAM
= 36,
141 REPAIR_PUT_ROW_DIFF_WITH_RPC_STREAM
= 37,
142 REPAIR_GET_FULL_ROW_HASHES_WITH_RPC_STREAM
= 38,
148 GOSSIP_GET_ENDPOINT_STATES
= 44,
150 RAFT_SEND_SNAPSHOT
= 46,
151 RAFT_APPEND_ENTRIES
= 47,
152 RAFT_APPEND_ENTRIES_REPLY
= 48,
153 RAFT_VOTE_REQUEST
= 49,
154 RAFT_VOTE_REPLY
= 50,
155 RAFT_TIMEOUT_NOW
= 51,
156 RAFT_READ_QUORUM
= 52,
157 RAFT_READ_QUORUM_REPLY
= 53,
158 RAFT_EXECUTE_READ_BARRIER_ON_LEADER
= 54,
160 RAFT_MODIFY_CONFIG
= 56,
161 GROUP0_PEER_EXCHANGE
= 57,
162 GROUP0_MODIFY_CONFIG
= 58,
163 REPAIR_UPDATE_SYSTEM_TABLE
= 59,
164 REPAIR_FLUSH_HINTS_BATCHLOG
= 60,
165 MAPREDUCE_REQUEST
= 61,
166 GET_GROUP0_UPGRADE_STATE
= 62,
168 RAFT_TOPOLOGY_CMD
= 64,
169 RAFT_PULL_SNAPSHOT
= 65,
170 TABLET_STREAM_DATA
= 66,
172 JOIN_NODE_REQUEST
= 68,
173 JOIN_NODE_RESPONSE
= 69,
174 TABLET_STREAM_FILES
= 70,
176 TABLE_LOAD_STATS
= 72,
177 JOIN_NODE_QUERY
= 73,
178 TASKS_GET_CHILDREN
= 74,
182 static const val64_string packettypenames
[] = {
183 {CLIENT_ID
, "CLIENT_ID"},
184 {MUTATION
, "MUTATION"},
185 {MUTATION_DONE
, "MUTATION_DONE"},
186 {READ_DATA
, "READ_DATA"},
187 {READ_MUTATION_DATA
, "READ_MUTATION_DATA"},
188 {READ_DIGEST
, "READ_DIGEST"},
189 {GOSSIP_DIGEST_SYN
, "GOSSIP_DIGEST_SYN"},
190 {GOSSIP_DIGEST_ACK
, "GOSSIP_DIGEST_ACK"},
191 {GOSSIP_DIGEST_ACK2
, "GOSSIP_DIGEST_ACK2"},
192 {GOSSIP_ECHO
, "GOSSIP_ECHO"},
193 {GOSSIP_SHUTDOWN
, "GOSSIP_SHUTDOWN"},
194 {DEFINITIONS_UPDATE
, "DEFINITIONS_UPDATE"},
195 {TRUNCATE
, "TRUNCATE"},
196 {REPLICATION_FINISHED
, "REPLICATION_FINISHED"},
197 {MIGRATION_REQUEST
, "MIGRATION_REQUEST"},
198 {PREPARE_MESSAGE
, "PREPARE_MESSAGE"},
199 {PREPARE_DONE_MESSAGE
, "PREPARE_DONE_MESSAGE"},
200 {STREAM_MUTATION
, "STREAM_MUTATION"},
201 {STREAM_MUTATION_DONE
, "STREAM_MUTATION_DONE"},
202 {COMPLETE_MESSAGE
, "COMPLETE_MESSAGE"},
203 {REPAIR_CHECKSUM_RANGE
, "REPAIR_CHECKSUM_RANGE"},
204 {GET_SCHEMA_VERSION
, "GET_SCHEMA_VERSION"},
205 {SCHEMA_CHECK
, "SCHEMA_CHECK"},
206 {COUNTER_MUTATION
, "COUNTER_MUTATION"},
207 {MUTATION_FAILED
, "MUTATION_FAILED"},
208 {STREAM_MUTATION_FRAGMENTS
, "STREAM_MUTATION_FRAGMENTS"},
209 {REPAIR_ROW_LEVEL_START
, "REPAIR_ROW_LEVEL_START"},
210 {REPAIR_ROW_LEVEL_STOP
, "REPAIR_ROW_LEVEL_STOP"},
211 {REPAIR_GET_FULL_ROW_HASHES
, "REPAIR_GET_FULL_ROW_HASHES"},
212 {REPAIR_GET_COMBINED_ROW_HASH
, "REPAIR_GET_COMBINED_ROW_HASH"},
213 {REPAIR_GET_SYNC_BOUNDARY
, "REPAIR_GET_SYNC_BOUNDARY"},
214 {REPAIR_GET_ROW_DIFF
, "REPAIR_GET_ROW_DIFF"},
215 {REPAIR_PUT_ROW_DIFF
, "REPAIR_PUT_ROW_DIFF"},
216 {REPAIR_GET_ESTIMATED_PARTITIONS
, "REPAIR_GET_ESTIMATED_PARTITIONS"},
217 {REPAIR_SET_ESTIMATED_PARTITIONS
, "REPAIR_SET_ESTIMATED_PARTITIONS"},
218 {REPAIR_GET_DIFF_ALGORITHMS
, "REPAIR_GET_DIFF_ALGORITHMS"},
219 {REPAIR_GET_ROW_DIFF_WITH_RPC_STREAM
, "REPAIR_GET_ROW_DIFF_WITH_RPC_STREAM"},
220 {REPAIR_PUT_ROW_DIFF_WITH_RPC_STREAM
, "REPAIR_PUT_ROW_DIFF_WITH_RPC_STREAM"},
221 {REPAIR_GET_FULL_ROW_HASHES_WITH_RPC_STREAM
, "REPAIR_GET_FULL_ROW_HASHES_WITH_RPC_STREAM"},
222 {PAXOS_PREPARE
, "PAXOS_PREPARE"},
223 {PAXOS_ACCEPT
, "PAXOS_ACCEPT"},
224 {PAXOS_LEARN
, "PAXOS_LEARN"},
225 {HINT_MUTATION
, "HINT_MUTATION"},
226 {PAXOS_PRUNE
, "PAXOS_PRUNE"},
227 {GOSSIP_GET_ENDPOINT_STATES
, "GOSSIP_GET_ENDPOINT_STATES"},
228 {NODE_OPS_CMD
, "NODE_OPS_CMD"},
229 {RAFT_SEND_SNAPSHOT
, "RAFT_SEND_SNAPSHOT"},
230 {RAFT_APPEND_ENTRIES
, "RAFT_APPEND_ENTRIES"},
231 {RAFT_APPEND_ENTRIES_REPLY
, "RAFT_APPEND_ENTRIES_REPLY"},
232 {RAFT_VOTE_REQUEST
, "RAFT_VOTE_REQUEST"},
233 {RAFT_VOTE_REPLY
, "RAFT_VOTE_REPLY"},
234 {RAFT_TIMEOUT_NOW
, "RAFT_TIMEOUT_NOW"},
235 {RAFT_READ_QUORUM
, "RAFT_READ_QUORUM"},
236 {RAFT_READ_QUORUM_REPLY
, "RAFT_READ_QUORUM_REPLY"},
237 {RAFT_EXECUTE_READ_BARRIER_ON_LEADER
, "RAFT_EXECUTE_READ_BARRIER_ON_LEADER"},
238 {RAFT_ADD_ENTRY
, "RAFT_ADD_ENTRY"},
239 {RAFT_MODIFY_CONFIG
, "RAFT_MODIFY_CONFIG"},
240 {GROUP0_PEER_EXCHANGE
, "GROUP0_PEER_EXCHANGE"},
241 {GROUP0_MODIFY_CONFIG
, "GROUP0_MODIFY_CONFIG"},
242 {REPAIR_UPDATE_SYSTEM_TABLE
, "REPAIR_UPDATE_SYSTEM_TABLE"},
243 {REPAIR_FLUSH_HINTS_BATCHLOG
, "REPAIR_FLUSH_HINTS_BATCHLOG"},
244 {MAPREDUCE_REQUEST
, "MAPREDUCE_REQUEST"},
245 {GET_GROUP0_UPGRADE_STATE
, "GET_GROUP0_UPGRADE_STATE"},
246 {DIRECT_FD_PING
, "DIRECT_FD_PING"},
247 {RAFT_TOPOLOGY_CMD
, "RAFT_TOPOLOGY_CMD"},
248 {RAFT_PULL_SNAPSHOT
, "RAFT_PULL_SNAPSHOT"},
249 {TABLET_STREAM_DATA
, "TABLET_STREAM_DATA"},
250 {TABLET_CLEANUP
, "TABLET_CLEANUP"},
251 {JOIN_NODE_REQUEST
, "JOIN_NODE_REQUEST"},
252 {JOIN_NODE_RESPONSE
, "JOIN_NODE_RESPONSE"},
253 {TABLET_STREAM_FILES
, "TABLET_STREAM_FILES"},
254 {STREAM_BLOB
, "STREAM_BLOB"},
255 {TABLE_LOAD_STATS
, "TABLE_LOAD_STATS"},
256 {JOIN_NODE_QUERY
, "JOIN_NODE_QUERY"},
257 {TASKS_GET_CHILDREN
, "TASKS_GET_CHILDREN"},
263 TIMEOUT_PROPAGATION
= 1,
267 HANDLER_DURATION
= 5,
270 static const value_string feature_names
[] = {
271 {COMPRESSION
, "Compression"},
272 {TIMEOUT_PROPAGATION
, "Timeout propagation"},
273 {CONNECTION_ID
, "Connection ID"},
274 {STREAM_PARENT
, "Stream parent"},
275 {ISOLATION
, "Isolation"},
276 {HANDLER_DURATION
, "Handler duration"},
281 looks_like_rpc_negotiation(tvbuff_t
*tvb
) {
282 return tvb_memeql(tvb
, 0, (const uint8_t *)"SSTARRPC", 8) == 0;
286 looks_like_response(uint64_t verb_type
, uint32_t len
) {
287 return verb_type
>= LAST
|| len
> 64*1024*1024;
292 uint32_t request_frame_num
;
293 uint32_t response_frame_num
;
294 } request_response_t
;
297 get_scylla_pdu_len(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset
, void *data _U_
)
299 uint64_t verb_type
= LAST
;
301 unsigned int reported_len
;
302 if (looks_like_rpc_negotiation(tvb
)) {
303 return tvb_get_letohl(tvb
, offset
+ SCYLLA_NEGOTIATION_LEN_OFFSET
) + SCYLLA_NEGOTIATION_SIZE
;
306 reported_len
= tvb_reported_length(tvb
);
309 if (reported_len
== tvb_get_letohl(tvb
, 0) + 4)
310 return reported_len
- 4;
312 if (reported_len
>= SCYLLA_HEADER_SIZE
) {
313 plen
= tvb_get_letohl(tvb
, offset
+ SCYLLA_HEADER_LEN_OFFSET
);
314 verb_type
= tvb_get_letoh64(tvb
, offset
+ SCYLLA_HEADER_VERB_OFFSET
);
317 if (looks_like_response(verb_type
, plen
)) {
318 return tvb_get_letohl(tvb
, offset
+ SCYLLA_RESPONSE_LEN_OFFSET
) + SCYLLA_RESPONSE_SIZE
;
321 return plen
+ SCYLLA_HEADER_SIZE
;
325 dissect_scylla_negotiation_pdu(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*scylla_tree
)
328 uint32_t feature_number
, feature_len
;
330 proto_tree
*scylla_features_tree
;
331 uint32_t len
= tvb_get_letohl(tvb
, offset
+ SCYLLA_NEGOTIATION_LEN_OFFSET
);
333 proto_tree
*scylla_negotiation_tree
= proto_tree_add_subtree(scylla_tree
, tvb
, offset
,
334 len
+ SCYLLA_NEGOTIATION_SIZE
, ett_scylla_negotiation
, NULL
, "Protocol negotiation");
335 proto_tree_add_item(scylla_negotiation_tree
, hf_scylla_negotiation_magic
, tvb
, offset
, 8, ENC_ASCII
);
337 proto_tree_add_item(scylla_negotiation_tree
, hf_scylla_negotiation_size
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
339 scylla_features_tree
= proto_tree_add_subtree(scylla_negotiation_tree
, tvb
, offset
, len
, ett_scylla_negotiation_features
, NULL
, "Negotiation features");
341 proto_tree_add_item_ret_uint(scylla_features_tree
, hf_scylla_feature_number
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
, &feature_number
);
343 proto_tree_add_item_ret_uint(scylla_features_tree
, hf_scylla_feature_len
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
, &feature_len
);
346 if (feature_len
> 0) {
347 switch (feature_number
)
351 if (feature_len
== 8)
352 proto_tree_add_item_ret_uint64(scylla_features_tree
, hf_scylla_connection_id
, tvb
, offset
, 8, ENC_LITTLE_ENDIAN
, &conn_id
);
355 proto_tree_add_item(scylla_features_tree
, hf_scylla_isolation_cookie
, tvb
, offset
, feature_len
, ENC_NA
);
358 proto_tree_add_item(scylla_features_tree
, hf_scylla_feature_data
, tvb
, offset
, feature_len
, ENC_NA
);
362 offset
+= feature_len
;
366 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "Scylla");
367 col_set_str(pinfo
->cinfo
, COL_INFO
, "Protocol negotiation");
368 return tvb_reported_length(tvb
);
372 dissect_scylla_response_pdu(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*scylla_tree
, request_response_t
*req_resp
)
375 uint32_t len
= tvb_get_letohl(tvb
, offset
+ SCYLLA_RESPONSE_LEN_OFFSET
) + SCYLLA_RESPONSE_SIZE
;
377 /* Add response subtree */
378 proto_item
*response_ti
= proto_tree_add_string_format(scylla_tree
, hf_scylla_response
,
379 tvb
, offset
, len
, "", "Response");
380 proto_tree
*scylla_response_tree
= proto_item_add_subtree(response_ti
, ett_scylla_response
);
385 proto_tree_add_item_ret_uint64(scylla_response_tree
, hf_scylla_msg_id
, tvb
, offset
+ resp_offset
, 8, ENC_LITTLE_ENDIAN
, &msg_id
);
387 proto_tree_add_item(scylla_response_tree
, hf_scylla_response_size
, tvb
, offset
+ resp_offset
, 4, ENC_LITTLE_ENDIAN
);
389 proto_tree_add_item(scylla_response_tree
, hf_scylla_payload
, tvb
, offset
+ resp_offset
, len
- resp_offset
, ENC_NA
);
391 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "Scylla");
393 /* Fill in the response frame */
394 req_resp
->response_frame_num
= pinfo
->num
;
396 proto_item
*verb_item
= proto_tree_add_uint64(scylla_response_tree
, hf_scylla_verb
, tvb
, offset
+ len
, 8, req_resp
->verb_type
);
397 proto_item_set_generated(verb_item
);
398 proto_item
*req
= proto_tree_add_uint(scylla_tree
, hf_scylla_response_request_frame
, tvb
, 0, 0, req_resp
->request_frame_num
);
399 proto_item_set_generated(req
);
401 proto_item_append_text(response_ti
, " (msg_id=%" PRIu64
", %s)",
402 msg_id
, val64_to_str(req_resp
->verb_type
, packettypenames
, "Unknown (0x%02x)"));
404 col_clear(pinfo
->cinfo
, COL_INFO
);
405 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Response for %s",
406 val64_to_str(req_resp
->verb_type
, packettypenames
, "Unknown (0x%02x)"));
408 col_set_str(pinfo
->cinfo
, COL_INFO
, "Response for unknown packet");
410 return tvb_reported_length(tvb
);
414 dissect_scylla_msg_pdu(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*scylla_tree
, proto_item
*ti
, uint64_t verb_type
, uint32_t len
, request_response_t
*req_resp
)
418 /* Add request subtree */
419 proto_item
*request_ti
= proto_tree_add_string_format(scylla_tree
, hf_scylla_request
,
420 tvb
, offset
, SCYLLA_HEADER_SIZE
,
422 val64_to_str(verb_type
, packettypenames
, "Unknown (0x%02x)"));
423 proto_tree
*scylla_header_tree
= proto_item_add_subtree(request_ti
, ett_scylla_response
);
425 proto_tree_add_item(scylla_header_tree
, hf_scylla_timeout
, tvb
, offset
, 8, ENC_LITTLE_ENDIAN
);
427 proto_item_append_text(ti
, ", Type %s", val64_to_str(verb_type
, packettypenames
, "Unknown (0x%02x)"));
428 proto_tree_add_item(scylla_header_tree
, hf_scylla_verb
, tvb
, offset
, 8, ENC_LITTLE_ENDIAN
);
431 proto_tree_add_item_ret_uint64(scylla_header_tree
, hf_scylla_msg_id
, tvb
, offset
, 8, ENC_LITTLE_ENDIAN
, &msg_id
);
433 proto_tree_add_item(scylla_header_tree
, hf_scylla_len
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
436 proto_item_append_text(request_ti
, " (msg_id=%" PRIu64
")", msg_id
);
440 proto_tree
* scylla_mut_tree
= proto_tree_add_subtree(scylla_tree
, tvb
, offset
,
441 len
, ett_scylla_mut
, NULL
, "Mutation");
445 proto_tree_add_item(scylla_mut_tree
, hf_scylla_mut_size1
, tvb
, offset
+ mut_offset
, 4, ENC_LITTLE_ENDIAN
);
447 proto_tree_add_item(scylla_mut_tree
, hf_scylla_mut_size2
, tvb
, offset
+ mut_offset
, 4, ENC_LITTLE_ENDIAN
);
449 proto_tree_add_item(scylla_mut_tree
, hf_scylla_mut_table_id
, tvb
, offset
+ mut_offset
, 16, ENC_NA
);
451 proto_tree_add_item(scylla_mut_tree
, hf_scylla_mut_schema_id
, tvb
, offset
+ mut_offset
, 16, ENC_NA
);
453 proto_tree_add_item_ret_uint(scylla_mut_tree
, hf_scylla_mut_len_pkeys
, tvb
, offset
+ mut_offset
, 4, ENC_LITTLE_ENDIAN
, &len_keys
);
455 proto_tree
* scylla_mut_pkey_tree
= proto_tree_add_subtree(scylla_mut_tree
, tvb
, offset
+ mut_offset
,
456 len
- mut_offset
, ett_scylla_mut_pkey
, NULL
, "Partition key");
457 proto_tree_add_item_ret_uint(scylla_mut_pkey_tree
, hf_scylla_mut_num_pkeys
, tvb
, offset
+ mut_offset
, 4, ENC_LITTLE_ENDIAN
, &num_keys
);
460 for (i
= 0; i
< num_keys
; ++i
) {
461 uint32_t len_pkey
= tvb_get_letohl(tvb
, offset
+ mut_offset
);
462 proto_tree_add_item(scylla_mut_pkey_tree
, hf_scylla_mut_len_pkey
, tvb
, offset
+ mut_offset
, 4, ENC_LITTLE_ENDIAN
);
464 proto_tree_add_item(scylla_mut_pkey_tree
, hf_scylla_mut_pkey
, tvb
, offset
+ mut_offset
, len_pkey
, ENC_NA
);
465 mut_offset
+= len_pkey
;
467 // TODO: dissect further
468 proto_tree_add_item(scylla_mut_tree
, hf_scylla_payload
, tvb
, offset
+ mut_offset
, len
- mut_offset
, ENC_NA
);
472 proto_tree
* scylla_read_tree
= proto_tree_add_subtree(scylla_tree
, tvb
, offset
,
473 len
, ett_scylla_read_data
, NULL
, "Read data");
476 proto_tree_add_item(scylla_read_tree
, hf_scylla_read_data_timeout
, tvb
, offset
+ rd_offset
, 4, ENC_LITTLE_ENDIAN
);
478 proto_tree_add_item(scylla_read_tree
, hf_scylla_read_data_table_id
, tvb
, offset
+ rd_offset
, 16, ENC_NA
);
480 proto_tree_add_item(scylla_read_tree
, hf_scylla_read_data_schema_version
, tvb
, offset
+ rd_offset
, 16, ENC_NA
);
482 //TODO: dissect further
483 proto_tree_add_item(scylla_read_tree
, hf_scylla_payload
, tvb
, offset
+ rd_offset
, len
- rd_offset
, ENC_NA
);
487 // Generic payload. TODO: dissect
488 proto_tree_add_item(scylla_tree
, hf_scylla_payload
, tvb
, offset
, len
, ENC_NA
);
492 /* req_resp will only be set if fd was already visited (PINFO_FD_VISITED(pinfo)) */
494 if (req_resp
->response_frame_num
> 0) {
495 proto_item
*rep
= proto_tree_add_uint(scylla_tree
, hf_scylla_request_response_frame
, tvb
, 0, 0, req_resp
->response_frame_num
);
496 proto_item_set_generated(rep
);
498 expert_add_info(pinfo
, request_ti
, &ei_scylla_response_missing
);
502 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "Scylla");
503 col_clear(pinfo
->cinfo
, COL_INFO
);
504 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Request %s",
505 val64_to_str(verb_type
, packettypenames
, "Unknown (0x%02x)"));
506 return tvb_reported_length(tvb
);
510 response_expected(uint64_t verb_type
)
513 case GOSSIP_DIGEST_SYN
:
514 case GOSSIP_DIGEST_ACK
:
515 case GOSSIP_DIGEST_ACK2
:
516 case GOSSIP_SHUTDOWN
:
517 case DEFINITIONS_UPDATE
:
520 case MUTATION_FAILED
:
531 dissect_scylla_pdu(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
534 conversation_t
*conversation
;
535 wmem_map_t
*conv_map
;
537 proto_item
*ti
= proto_tree_add_item(tree
, proto_scylla
, tvb
, 0, -1, ENC_NA
);
538 proto_tree
*scylla_tree
= proto_item_add_subtree(ti
, ett_scylla
);
540 uint64_t verb_type
= LAST
;
543 if (looks_like_rpc_negotiation(tvb
)) {
544 return dissect_scylla_negotiation_pdu(tvb
, pinfo
, scylla_tree
);
547 if (tvb_reported_length(tvb
) >= SCYLLA_HEADER_SIZE
) {
548 verb_type
= tvb_get_letoh64(tvb
, offset
+ SCYLLA_HEADER_VERB_OFFSET
);
549 len
= tvb_get_letohl(tvb
, offset
+ SCYLLA_HEADER_LEN_OFFSET
);
552 conversation
= find_or_create_conversation(pinfo
);
553 conv_map
= (wmem_map_t
*)conversation_get_proto_data(conversation
, proto_scylla
);
554 if (conv_map
== NULL
) {
555 conv_map
= wmem_map_new(wmem_file_scope(), wmem_int64_hash
, g_int64_equal
);
556 conversation_add_proto_data(conversation
, proto_scylla
, conv_map
);
559 if (looks_like_response(verb_type
, len
)) {
562 msg_id
= tvb_get_letoh64(tvb
, offset
+ SCYLLA_RESPONSE_MSG_ID_OFFSET
);
563 req_resp
= wmem_map_lookup(conv_map
, &msg_id
);
564 return dissect_scylla_response_pdu(tvb
, pinfo
, scylla_tree
, (request_response_t
*)req_resp
);
567 uint64_t msg_id
= tvb_get_letoh64(tvb
, offset
+ SCYLLA_HEADER_MSG_ID_OFFSET
);
568 void *req_resp
= NULL
;
570 if (response_expected(verb_type
)) {
571 if (!PINFO_FD_VISITED(pinfo
)) {
572 uint64_t *key
= wmem_new(wmem_file_scope(), uint64_t);
573 request_response_t
*val
= wmem_new(wmem_file_scope(), request_response_t
);
575 val
->verb_type
= verb_type
;
576 val
->request_frame_num
= pinfo
->num
;
577 wmem_map_insert(conv_map
, key
, val
);
579 req_resp
= wmem_map_lookup(conv_map
, &msg_id
);
583 return dissect_scylla_msg_pdu(tvb
, pinfo
, scylla_tree
, ti
, verb_type
, len
, (request_response_t
*)req_resp
);
587 dissect_scylla(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
589 tcp_dissect_pdus(tvb
, pinfo
, tree
, scylla_desegment
, SCYLLA_NEGOTIATION_SIZE
,
590 get_scylla_pdu_len
, dissect_scylla_pdu
, data
);
591 return tvb_reported_length(tvb
);
595 proto_register_scylla(void)
597 static hf_register_info hf
[] = {
599 { &hf_scylla_request
, { "request", "scylla.request", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
} },
600 { &hf_scylla_request_response_frame
, { "Response frame", "scylla.request.response", FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE
), 0x0, NULL
, HFILL
} },
601 { &hf_scylla_timeout
, { "RPC timeout", "scylla.timeout", FT_UINT64
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
602 { &hf_scylla_verb
, { "verb", "scylla.verb", FT_UINT64
, BASE_DEC
|BASE_VAL64_STRING
, VALS64(packettypenames
), 0x0, NULL
, HFILL
} },
603 { &hf_scylla_msg_id
, { "msg id", "scylla.msg_id", FT_UINT64
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
604 { &hf_scylla_len
, { "packet length", "scylla.len", FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
605 { &hf_scylla_payload
, { "payload", "scylla.payload", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
} },
606 { &hf_scylla_response
, { "response", "scylla.response", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
} },
607 { &hf_scylla_response_size
, { "response size", "scylla.response.size", FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
608 { &hf_scylla_response_request_frame
, { "Request frame", "scylla.response.request", FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST
), 0x0, NULL
, HFILL
} },
609 { &hf_scylla_negotiation_magic
, { "negotiation magic sequence", "scylla.negotiation.magic", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
} },
610 { &hf_scylla_negotiation_size
, { "negotiation size", "scylla.negotiation.size", FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
611 { &hf_scylla_feature_number
, { "feature number", "scylla.negotiation.feature.number", FT_UINT32
, BASE_DEC
, VALS(feature_names
), 0x0, NULL
, HFILL
} },
612 { &hf_scylla_feature_len
, { "feature len", "scylla.negotiation.feature.len", FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
613 { &hf_scylla_feature_data
, { "feature data", "scylla.negotiation.feature.data", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
} },
614 { &hf_scylla_connection_id
, { "connection ID", "scylla.connection_id", FT_UINT64
, BASE_HEX
, NULL
, 0x0, NULL
, HFILL
} },
615 { &hf_scylla_isolation_cookie
, { "isolation cookie", "scylla.isolation_cookie", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
} },
616 { &hf_scylla_streaming_len
, { "streaming length", "scylla.streaming.length", FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
618 { &hf_scylla_mut_size1
, { "mutation size 1", "scylla.mut.size1", FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
619 { &hf_scylla_mut_size2
, { "mutation size 2", "scylla.mut.size2", FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
620 { &hf_scylla_mut_table_id
, { "mutation table id", "scylla.mut.table_id", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
} },
621 { &hf_scylla_mut_schema_id
, { "mutation schema id", "scylla.mut.schema_id", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
} },
622 { &hf_scylla_mut_len_pkeys
, { "size of partition keys payload", "scylla.mut.len_pkeys", FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
623 { &hf_scylla_mut_num_pkeys
, { "number of partition keys", "scylla.mut.num_pkeys", FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
624 { &hf_scylla_mut_len_pkey
, { "length of a partition key", "scylla.mut.len_pkey", FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
625 { &hf_scylla_mut_pkey
, { "partition key", "scylla.mut.pkey", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
} },
627 { &hf_scylla_read_data_timeout
, { "timeout", "scylla.read_data.timeout", FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
} },
628 { &hf_scylla_read_data_table_id
, { "table ID", "scylla.read_data.table_id", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
} },
629 { &hf_scylla_read_data_schema_version
, { "Schema version", "scylla.read_data.schema_version", FT_BYTES
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
} },
633 static ei_register_info ei
[] = {
634 { &ei_scylla_response_missing
,
635 { "scylla.ei_scylla_response_missing",
636 PI_COMMENTS_GROUP
, PI_NOTE
, "Response has not arrived yet", EXPFILL
}},
639 /* Setup protocol subtree array */
640 static int *ett
[] = {
643 &ett_scylla_response
,
644 &ett_scylla_negotiation
,
645 &ett_scylla_negotiation_features
,
646 &ett_sclla_streaming
,
648 &ett_scylla_mut_pkey
,
649 &ett_scylla_read_data
,
652 expert_module_t
* expert_scylla
;
654 proto_scylla
= proto_register_protocol("Scylla RPC protocol", "Scylla", "scylla");
655 module_t
* scylla_module
= prefs_register_protocol(proto_scylla
, NULL
);
656 prefs_register_bool_preference(scylla_module
, "desegment",
657 "Desegment all Scylla messages spanning multiple TCP segments",
658 "Whether Scylla dissector should desegment all messages spanning multiple TCP segments",
661 proto_register_field_array(proto_scylla
, hf
, array_length(hf
));
662 proto_register_subtree_array(ett
, array_length(ett
));
663 expert_scylla
= expert_register_protocol(proto_scylla
);
664 expert_register_field_array(expert_scylla
, ei
, array_length(ei
));
666 scylla_handle
= register_dissector("scylla", dissect_scylla
, proto_scylla
);
670 proto_reg_handoff_scylla(void)
672 dissector_add_uint_with_preference("tcp.port", SCYLLA_PORT
, scylla_handle
);
676 * Editor modelines - https://www.wireshark.org/tools/modelines.html
681 * indent-tabs-mode: nil
684 * vi: set shiftwidth=4 tabstop=8 expandtab:
685 * :indentSize=4:tabSize=8:noTabs=true: