Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-scylla.c
blob0095130fb2f564fbd596fb1b1d941c3d6528016d
1 /* packet-scylla.c
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
23 #include <config.h>
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
70 // Mutation
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;
80 // Read data
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;
99 enum scylla_packets {
100 CLIENT_ID = 0,
101 MUTATION = 1,
102 MUTATION_DONE = 2,
103 READ_DATA = 3,
104 READ_MUTATION_DATA = 4,
105 READ_DIGEST = 5,
106 // Used by gossip
107 GOSSIP_DIGEST_SYN = 6,
108 GOSSIP_DIGEST_ACK = 7,
109 GOSSIP_DIGEST_ACK2 = 8,
110 GOSSIP_ECHO = 9,
111 GOSSIP_SHUTDOWN = 10,
112 // end of gossip verb
113 DEFINITIONS_UPDATE = 11,
114 TRUNCATE = 12,
115 REPLICATION_FINISHED = 13,
116 MIGRATION_REQUEST = 14,
117 // Used by streaming
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,
126 SCHEMA_CHECK = 22,
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,
143 PAXOS_PREPARE = 39,
144 PAXOS_ACCEPT = 40,
145 PAXOS_LEARN = 41,
146 HINT_MUTATION = 42,
147 PAXOS_PRUNE = 43,
148 GOSSIP_GET_ENDPOINT_STATES = 44,
149 NODE_OPS_CMD = 45,
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,
159 RAFT_ADD_ENTRY = 55,
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,
167 DIRECT_FD_PING = 63,
168 RAFT_TOPOLOGY_CMD = 64,
169 RAFT_PULL_SNAPSHOT = 65,
170 TABLET_STREAM_DATA = 66,
171 TABLET_CLEANUP = 67,
172 JOIN_NODE_REQUEST = 68,
173 JOIN_NODE_RESPONSE = 69,
174 TABLET_STREAM_FILES = 70,
175 STREAM_BLOB = 71,
176 TABLE_LOAD_STATS = 72,
177 JOIN_NODE_QUERY = 73,
178 TASKS_GET_CHILDREN = 74,
179 LAST = 75,
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"},
258 {0, NULL}
261 enum features {
262 COMPRESSION = 0,
263 TIMEOUT_PROPAGATION = 1,
264 CONNECTION_ID = 2,
265 STREAM_PARENT = 3,
266 ISOLATION = 4,
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"},
277 {0, NULL}
280 static bool
281 looks_like_rpc_negotiation(tvbuff_t *tvb) {
282 return tvb_memeql(tvb, 0, (const uint8_t *)"SSTARRPC", 8) == 0;
285 static bool
286 looks_like_response(uint64_t verb_type, uint32_t len) {
287 return verb_type >= LAST || len > 64*1024*1024;
290 typedef struct {
291 uint64_t verb_type;
292 uint32_t request_frame_num;
293 uint32_t response_frame_num;
294 } request_response_t;
296 static unsigned
297 get_scylla_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
299 uint64_t verb_type = LAST;
300 uint32_t plen = 0;
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);
308 /* streaming */
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;
324 static int
325 dissect_scylla_negotiation_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *scylla_tree)
327 int offset = 0;
328 uint32_t feature_number, feature_len;
329 uint64_t conn_id;
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);
336 offset += 8;
337 proto_tree_add_item(scylla_negotiation_tree, hf_scylla_negotiation_size, tvb, offset, 4, ENC_LITTLE_ENDIAN);
338 offset += 4;
339 scylla_features_tree = proto_tree_add_subtree(scylla_negotiation_tree, tvb, offset, len, ett_scylla_negotiation_features, NULL, "Negotiation features");
340 while (len > 0) {
341 proto_tree_add_item_ret_uint(scylla_features_tree, hf_scylla_feature_number, tvb, offset, 4, ENC_LITTLE_ENDIAN, &feature_number);
342 offset += 4;
343 proto_tree_add_item_ret_uint(scylla_features_tree, hf_scylla_feature_len, tvb, offset, 4, ENC_LITTLE_ENDIAN, &feature_len);
344 offset += 4;
345 len -= 8;
346 if (feature_len > 0) {
347 switch (feature_number)
349 case CONNECTION_ID:
350 case STREAM_PARENT:
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);
353 break;
354 case ISOLATION:
355 proto_tree_add_item(scylla_features_tree, hf_scylla_isolation_cookie, tvb, offset, feature_len, ENC_NA);
356 break;
357 default:
358 proto_tree_add_item(scylla_features_tree, hf_scylla_feature_data, tvb, offset, feature_len, ENC_NA);
359 break;
361 len -= feature_len;
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);
371 static int
372 dissect_scylla_response_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *scylla_tree, request_response_t *req_resp)
374 int offset = 0;
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);
382 int resp_offset = 0;
384 uint64_t msg_id;
385 proto_tree_add_item_ret_uint64(scylla_response_tree, hf_scylla_msg_id, tvb, offset + resp_offset, 8, ENC_LITTLE_ENDIAN, &msg_id);
386 resp_offset += 8;
387 proto_tree_add_item(scylla_response_tree, hf_scylla_response_size, tvb, offset + resp_offset, 4, ENC_LITTLE_ENDIAN);
388 resp_offset += 4;
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");
392 if (req_resp) {
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)"));
407 } else {
408 col_set_str(pinfo->cinfo, COL_INFO, "Response for unknown packet");
410 return tvb_reported_length(tvb);
413 static int
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)
416 int offset = 0;
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,
421 "", "Header for %s",
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);
426 offset += 8;
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);
429 offset += 8;
430 uint64_t msg_id;
431 proto_tree_add_item_ret_uint64(scylla_header_tree, hf_scylla_msg_id, tvb, offset, 8, ENC_LITTLE_ENDIAN, &msg_id);
432 offset += 8;
433 proto_tree_add_item(scylla_header_tree, hf_scylla_len, tvb, offset, 4, ENC_LITTLE_ENDIAN);
434 offset += 4;
436 proto_item_append_text(request_ti, " (msg_id=%" PRIu64 ")", msg_id);
438 switch (verb_type) {
439 case MUTATION: {
440 proto_tree* scylla_mut_tree = proto_tree_add_subtree(scylla_tree, tvb, offset,
441 len, ett_scylla_mut, NULL, "Mutation");
442 int mut_offset = 0;
443 uint32_t len_keys;
444 uint32_t num_keys;
445 proto_tree_add_item(scylla_mut_tree, hf_scylla_mut_size1, tvb, offset + mut_offset, 4, ENC_LITTLE_ENDIAN);
446 mut_offset += 4;
447 proto_tree_add_item(scylla_mut_tree, hf_scylla_mut_size2, tvb, offset + mut_offset, 4, ENC_LITTLE_ENDIAN);
448 mut_offset += 4;
449 proto_tree_add_item(scylla_mut_tree, hf_scylla_mut_table_id, tvb, offset + mut_offset, 16, ENC_NA);
450 mut_offset += 16;
451 proto_tree_add_item(scylla_mut_tree, hf_scylla_mut_schema_id, tvb, offset + mut_offset, 16, ENC_NA);
452 mut_offset += 16;
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);
454 mut_offset += 4;
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);
458 mut_offset += 4;
459 unsigned i;
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);
463 mut_offset += 4;
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);
470 break;
471 case READ_DATA: {
472 proto_tree* scylla_read_tree = proto_tree_add_subtree(scylla_tree, tvb, offset,
473 len, ett_scylla_read_data, NULL, "Read data");
474 int rd_offset = 0;
476 proto_tree_add_item(scylla_read_tree, hf_scylla_read_data_timeout, tvb, offset + rd_offset, 4, ENC_LITTLE_ENDIAN);
477 rd_offset += 4;
478 proto_tree_add_item(scylla_read_tree, hf_scylla_read_data_table_id, tvb, offset + rd_offset, 16, ENC_NA);
479 rd_offset += 16;
480 proto_tree_add_item(scylla_read_tree, hf_scylla_read_data_schema_version, tvb, offset + rd_offset, 16, ENC_NA);
481 rd_offset += 16;
482 //TODO: dissect further
483 proto_tree_add_item(scylla_read_tree, hf_scylla_payload, tvb, offset + rd_offset, len - rd_offset, ENC_NA);
485 break;
486 default:
487 // Generic payload. TODO: dissect
488 proto_tree_add_item(scylla_tree, hf_scylla_payload, tvb, offset, len, ENC_NA);
489 break;
492 /* req_resp will only be set if fd was already visited (PINFO_FD_VISITED(pinfo)) */
493 if (req_resp) {
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);
497 } else {
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);
509 static bool
510 response_expected(uint64_t verb_type)
512 switch (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:
518 case MUTATION:
519 case MUTATION_DONE:
520 case MUTATION_FAILED:
521 case HINT_MUTATION:
522 case PAXOS_LEARN:
523 case PAXOS_PRUNE:
524 return false;
525 default:
526 return true;
530 static int
531 dissect_scylla_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
533 int offset = 0;
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;
541 uint32_t len = 0;
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)) {
560 void *req_resp;
561 uint64_t msg_id;
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);
574 *key = msg_id;
575 val->verb_type = verb_type;
576 val->request_frame_num = pinfo->num;
577 wmem_map_insert(conv_map, key, val);
578 } else {
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);
586 static int
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);
594 void
595 proto_register_scylla(void)
597 static hf_register_info hf[] = {
598 // RPC header
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 } },
617 // mutation verb
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 } },
626 // read_data verb
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[] = {
641 &ett_scylla,
642 &ett_scylla_header,
643 &ett_scylla_response,
644 &ett_scylla_negotiation,
645 &ett_scylla_negotiation_features,
646 &ett_sclla_streaming,
647 &ett_scylla_mut,
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",
659 &scylla_desegment);
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);
669 void
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
678 * Local variables:
679 * c-basic-offset: 4
680 * tab-width: 8
681 * indent-tabs-mode: nil
682 * End:
684 * vi: set shiftwidth=4 tabstop=8 expandtab:
685 * :indentSize=4:tabSize=8:noTabs=true: