/src/wireshark/epan/dissectors/packet-scylla.c
Line | Count | Source |
1 | | /* packet-scylla.c |
2 | | * Routines for Scylla RPC dissection |
3 | | * Copyright 2020 ScyllaDB, Piotr Sarna <sarna@scylladb.com> |
4 | | * |
5 | | * Wireshark - Network traffic analyzer |
6 | | * By Gerald Combs <gerald@wireshark.org> |
7 | | * Copyright 1998 Gerald Combs |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | |
12 | | /* |
13 | | * 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. |
16 | | * |
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 |
20 | | * |
21 | | */ |
22 | | |
23 | | #include <config.h> |
24 | | |
25 | | #include <epan/expert.h> |
26 | | #include <epan/packet.h> |
27 | | #include <epan/prefs.h> |
28 | | #include "packet-tcp.h" |
29 | | |
30 | | void proto_reg_handoff_scylla(void); |
31 | | void proto_register_scylla(void); |
32 | | |
33 | | static dissector_handle_t scylla_handle; |
34 | | |
35 | 16 | #define SCYLLA_PORT 0 /* Not IANA registered, 7000 is the expected value */ |
36 | | |
37 | 0 | #define SCYLLA_HEADER_SIZE 28 |
38 | 0 | #define SCYLLA_HEADER_VERB_OFFSET 8 |
39 | 0 | #define SCYLLA_HEADER_MSG_ID_OFFSET 16 |
40 | 0 | #define SCYLLA_HEADER_LEN_OFFSET 24 |
41 | | |
42 | 0 | #define SCYLLA_RESPONSE_SIZE 12 |
43 | 0 | #define SCYLLA_RESPONSE_MSG_ID_OFFSET 0 |
44 | 0 | #define SCYLLA_RESPONSE_LEN_OFFSET 8 |
45 | | |
46 | 0 | #define SCYLLA_NEGOTIATION_SIZE 12 |
47 | 0 | #define SCYLLA_NEGOTIATION_LEN_OFFSET 8 |
48 | | |
49 | | static int proto_scylla; |
50 | | |
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 |
69 | | |
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; |
79 | | |
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; |
84 | | |
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; |
94 | | |
95 | | static bool scylla_desegment = true; |
96 | | |
97 | | static expert_field ei_scylla_response_missing; |
98 | | |
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, |
180 | | }; |
181 | | |
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} |
259 | | }; |
260 | | |
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, |
268 | | }; |
269 | | |
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} |
278 | | }; |
279 | | |
280 | | static bool |
281 | 0 | looks_like_rpc_negotiation(tvbuff_t *tvb) { |
282 | 0 | return tvb_memeql(tvb, 0, (const uint8_t *)"SSTARRPC", 8) == 0; |
283 | 0 | } |
284 | | |
285 | | static bool |
286 | 0 | looks_like_response(uint64_t verb_type, uint32_t len) { |
287 | 0 | return verb_type >= LAST || len > 64*1024*1024; |
288 | 0 | } |
289 | | |
290 | | typedef struct { |
291 | | uint64_t verb_type; |
292 | | uint32_t request_frame_num; |
293 | | uint32_t response_frame_num; |
294 | | } request_response_t; |
295 | | |
296 | | static unsigned |
297 | | get_scylla_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) |
298 | 0 | { |
299 | 0 | uint64_t verb_type = LAST; |
300 | 0 | uint32_t plen = 0; |
301 | 0 | unsigned int reported_len; |
302 | 0 | if (looks_like_rpc_negotiation(tvb)) { |
303 | 0 | return tvb_get_letohl(tvb, offset + SCYLLA_NEGOTIATION_LEN_OFFSET) + SCYLLA_NEGOTIATION_SIZE; |
304 | 0 | } |
305 | | |
306 | 0 | reported_len = tvb_reported_length(tvb); |
307 | | |
308 | | /* streaming */ |
309 | 0 | if (reported_len == tvb_get_letohl(tvb, 0) + 4) |
310 | 0 | return reported_len - 4; |
311 | | |
312 | 0 | if (reported_len >= SCYLLA_HEADER_SIZE) { |
313 | 0 | plen = tvb_get_letohl(tvb, offset + SCYLLA_HEADER_LEN_OFFSET); |
314 | 0 | verb_type = tvb_get_letoh64(tvb, offset + SCYLLA_HEADER_VERB_OFFSET); |
315 | 0 | } |
316 | |
|
317 | 0 | if (looks_like_response(verb_type, plen)) { |
318 | 0 | return tvb_get_letohl(tvb, offset + SCYLLA_RESPONSE_LEN_OFFSET) + SCYLLA_RESPONSE_SIZE; |
319 | 0 | } |
320 | | |
321 | 0 | return plen + SCYLLA_HEADER_SIZE; |
322 | 0 | } |
323 | | |
324 | | static int |
325 | | dissect_scylla_negotiation_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *scylla_tree) |
326 | 0 | { |
327 | 0 | unsigned offset = 0; |
328 | 0 | uint32_t feature_number, feature_len; |
329 | 0 | uint64_t conn_id; |
330 | 0 | proto_tree *scylla_features_tree; |
331 | 0 | uint32_t len = tvb_get_letohl(tvb, offset + SCYLLA_NEGOTIATION_LEN_OFFSET); |
332 | |
|
333 | 0 | proto_tree *scylla_negotiation_tree = proto_tree_add_subtree(scylla_tree, tvb, offset, |
334 | 0 | len + SCYLLA_NEGOTIATION_SIZE, ett_scylla_negotiation, NULL, "Protocol negotiation"); |
335 | 0 | proto_tree_add_item(scylla_negotiation_tree, hf_scylla_negotiation_magic, tvb, offset, 8, ENC_ASCII); |
336 | 0 | offset += 8; |
337 | 0 | proto_tree_add_item(scylla_negotiation_tree, hf_scylla_negotiation_size, tvb, offset, 4, ENC_LITTLE_ENDIAN); |
338 | 0 | offset += 4; |
339 | 0 | scylla_features_tree = proto_tree_add_subtree(scylla_negotiation_tree, tvb, offset, len, ett_scylla_negotiation_features, NULL, "Negotiation features"); |
340 | 0 | while (len > 0) { |
341 | 0 | proto_tree_add_item_ret_uint(scylla_features_tree, hf_scylla_feature_number, tvb, offset, 4, ENC_LITTLE_ENDIAN, &feature_number); |
342 | 0 | offset += 4; |
343 | 0 | proto_tree_add_item_ret_uint(scylla_features_tree, hf_scylla_feature_len, tvb, offset, 4, ENC_LITTLE_ENDIAN, &feature_len); |
344 | 0 | offset += 4; |
345 | 0 | len -= 8; |
346 | 0 | if (feature_len > 0) { |
347 | 0 | switch (feature_number) |
348 | 0 | { |
349 | 0 | case CONNECTION_ID: |
350 | 0 | case STREAM_PARENT: |
351 | 0 | if (feature_len == 8) |
352 | 0 | proto_tree_add_item_ret_uint64(scylla_features_tree, hf_scylla_connection_id, tvb, offset, 8, ENC_LITTLE_ENDIAN, &conn_id); |
353 | 0 | break; |
354 | 0 | case ISOLATION: |
355 | 0 | proto_tree_add_item(scylla_features_tree, hf_scylla_isolation_cookie, tvb, offset, feature_len, ENC_NA); |
356 | 0 | break; |
357 | 0 | default: |
358 | 0 | proto_tree_add_item(scylla_features_tree, hf_scylla_feature_data, tvb, offset, feature_len, ENC_NA); |
359 | 0 | break; |
360 | 0 | } |
361 | 0 | len -= feature_len; |
362 | 0 | offset += feature_len; |
363 | 0 | } |
364 | 0 | } |
365 | | |
366 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "Scylla"); |
367 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "Protocol negotiation"); |
368 | 0 | return tvb_reported_length(tvb); |
369 | 0 | } |
370 | | |
371 | | static int |
372 | | dissect_scylla_response_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *scylla_tree, request_response_t *req_resp) |
373 | 0 | { |
374 | 0 | unsigned offset = 0; |
375 | 0 | uint32_t len = tvb_get_letohl(tvb, offset + SCYLLA_RESPONSE_LEN_OFFSET) + SCYLLA_RESPONSE_SIZE; |
376 | | |
377 | | /* Add response subtree */ |
378 | 0 | proto_item *response_ti = proto_tree_add_string_format(scylla_tree, hf_scylla_response, |
379 | 0 | tvb, offset, len, "", "Response"); |
380 | 0 | proto_tree *scylla_response_tree = proto_item_add_subtree(response_ti, ett_scylla_response); |
381 | |
|
382 | 0 | int resp_offset = 0; |
383 | |
|
384 | 0 | uint64_t msg_id; |
385 | 0 | proto_tree_add_item_ret_uint64(scylla_response_tree, hf_scylla_msg_id, tvb, offset + resp_offset, 8, ENC_LITTLE_ENDIAN, &msg_id); |
386 | 0 | resp_offset += 8; |
387 | 0 | proto_tree_add_item(scylla_response_tree, hf_scylla_response_size, tvb, offset + resp_offset, 4, ENC_LITTLE_ENDIAN); |
388 | 0 | resp_offset += 4; |
389 | 0 | proto_tree_add_item(scylla_response_tree, hf_scylla_payload, tvb, offset + resp_offset, len - resp_offset, ENC_NA); |
390 | |
|
391 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "Scylla"); |
392 | 0 | if (req_resp) { |
393 | | /* Fill in the response frame */ |
394 | 0 | req_resp->response_frame_num = pinfo->num; |
395 | |
|
396 | 0 | proto_item *verb_item = proto_tree_add_uint64(scylla_response_tree, hf_scylla_verb, tvb, offset + len, 8, req_resp->verb_type); |
397 | 0 | proto_item_set_generated(verb_item); |
398 | 0 | proto_item *req = proto_tree_add_uint(scylla_tree, hf_scylla_response_request_frame, tvb, 0, 0, req_resp->request_frame_num); |
399 | 0 | proto_item_set_generated(req); |
400 | |
|
401 | 0 | proto_item_append_text(response_ti, " (msg_id=%" PRIu64 ", %s)", |
402 | 0 | msg_id, val64_to_str_wmem(pinfo->pool, req_resp->verb_type, packettypenames, "Unknown (0x%02x)")); |
403 | |
|
404 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
405 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, "Response for %s", |
406 | 0 | val64_to_str_wmem(pinfo->pool, req_resp->verb_type, packettypenames, "Unknown (0x%02x)")); |
407 | 0 | } else { |
408 | 0 | col_set_str(pinfo->cinfo, COL_INFO, "Response for unknown packet"); |
409 | 0 | } |
410 | 0 | return tvb_reported_length(tvb); |
411 | 0 | } |
412 | | |
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) |
415 | 0 | { |
416 | 0 | unsigned offset = 0; |
417 | | |
418 | | /* Add request subtree */ |
419 | 0 | proto_item *request_ti = proto_tree_add_string_format(scylla_tree, hf_scylla_request, |
420 | 0 | tvb, offset, SCYLLA_HEADER_SIZE, |
421 | 0 | "", "Header for %s", |
422 | 0 | val64_to_str_wmem(pinfo->pool, verb_type, packettypenames, "Unknown (0x%02x)")); |
423 | 0 | proto_tree *scylla_header_tree = proto_item_add_subtree(request_ti, ett_scylla_response); |
424 | |
|
425 | 0 | proto_tree_add_item(scylla_header_tree, hf_scylla_timeout, tvb, offset, 8, ENC_LITTLE_ENDIAN); |
426 | 0 | offset += 8; |
427 | 0 | proto_item_append_text(ti, ", Type %s", val64_to_str_wmem(pinfo->pool, verb_type, packettypenames, "Unknown (0x%02x)")); |
428 | 0 | proto_tree_add_item(scylla_header_tree, hf_scylla_verb, tvb, offset, 8, ENC_LITTLE_ENDIAN); |
429 | 0 | offset += 8; |
430 | 0 | uint64_t msg_id; |
431 | 0 | proto_tree_add_item_ret_uint64(scylla_header_tree, hf_scylla_msg_id, tvb, offset, 8, ENC_LITTLE_ENDIAN, &msg_id); |
432 | 0 | offset += 8; |
433 | 0 | proto_tree_add_item(scylla_header_tree, hf_scylla_len, tvb, offset, 4, ENC_LITTLE_ENDIAN); |
434 | 0 | offset += 4; |
435 | |
|
436 | 0 | proto_item_append_text(request_ti, " (msg_id=%" PRIu64 ")", msg_id); |
437 | |
|
438 | 0 | switch (verb_type) { |
439 | 0 | case MUTATION: { |
440 | 0 | proto_tree* scylla_mut_tree = proto_tree_add_subtree(scylla_tree, tvb, offset, |
441 | 0 | len, ett_scylla_mut, NULL, "Mutation"); |
442 | 0 | int mut_offset = 0; |
443 | 0 | uint32_t len_keys; |
444 | 0 | uint32_t num_keys; |
445 | 0 | proto_tree_add_item(scylla_mut_tree, hf_scylla_mut_size1, tvb, offset + mut_offset, 4, ENC_LITTLE_ENDIAN); |
446 | 0 | mut_offset += 4; |
447 | 0 | proto_tree_add_item(scylla_mut_tree, hf_scylla_mut_size2, tvb, offset + mut_offset, 4, ENC_LITTLE_ENDIAN); |
448 | 0 | mut_offset += 4; |
449 | 0 | proto_tree_add_item(scylla_mut_tree, hf_scylla_mut_table_id, tvb, offset + mut_offset, 16, ENC_NA); |
450 | 0 | mut_offset += 16; |
451 | 0 | proto_tree_add_item(scylla_mut_tree, hf_scylla_mut_schema_id, tvb, offset + mut_offset, 16, ENC_NA); |
452 | 0 | mut_offset += 16; |
453 | 0 | 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 | 0 | mut_offset += 4; |
455 | 0 | proto_tree* scylla_mut_pkey_tree = proto_tree_add_subtree(scylla_mut_tree, tvb, offset + mut_offset, |
456 | 0 | len - mut_offset, ett_scylla_mut_pkey, NULL, "Partition key"); |
457 | 0 | 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 | 0 | mut_offset += 4; |
459 | 0 | unsigned i; |
460 | 0 | for (i = 0; i < num_keys; ++i) { |
461 | 0 | uint32_t len_pkey = tvb_get_letohl(tvb, offset + mut_offset); |
462 | 0 | proto_tree_add_item(scylla_mut_pkey_tree, hf_scylla_mut_len_pkey, tvb, offset + mut_offset, 4, ENC_LITTLE_ENDIAN); |
463 | 0 | mut_offset += 4; |
464 | 0 | proto_tree_add_item(scylla_mut_pkey_tree, hf_scylla_mut_pkey, tvb, offset + mut_offset, len_pkey, ENC_NA); |
465 | 0 | mut_offset += len_pkey; |
466 | 0 | } |
467 | | // TODO: dissect further |
468 | 0 | proto_tree_add_item(scylla_mut_tree, hf_scylla_payload, tvb, offset + mut_offset, len - mut_offset, ENC_NA); |
469 | 0 | } |
470 | 0 | break; |
471 | 0 | case READ_DATA: { |
472 | 0 | proto_tree* scylla_read_tree = proto_tree_add_subtree(scylla_tree, tvb, offset, |
473 | 0 | len, ett_scylla_read_data, NULL, "Read data"); |
474 | 0 | int rd_offset = 0; |
475 | |
|
476 | 0 | proto_tree_add_item(scylla_read_tree, hf_scylla_read_data_timeout, tvb, offset + rd_offset, 4, ENC_LITTLE_ENDIAN); |
477 | 0 | rd_offset += 4; |
478 | 0 | proto_tree_add_item(scylla_read_tree, hf_scylla_read_data_table_id, tvb, offset + rd_offset, 16, ENC_NA); |
479 | 0 | rd_offset += 16; |
480 | 0 | proto_tree_add_item(scylla_read_tree, hf_scylla_read_data_schema_version, tvb, offset + rd_offset, 16, ENC_NA); |
481 | 0 | rd_offset += 16; |
482 | | //TODO: dissect further |
483 | 0 | proto_tree_add_item(scylla_read_tree, hf_scylla_payload, tvb, offset + rd_offset, len - rd_offset, ENC_NA); |
484 | 0 | } |
485 | 0 | break; |
486 | 0 | default: |
487 | | // Generic payload. TODO: dissect |
488 | 0 | proto_tree_add_item(scylla_tree, hf_scylla_payload, tvb, offset, len, ENC_NA); |
489 | 0 | break; |
490 | 0 | } |
491 | | |
492 | | /* req_resp will only be set if fd was already visited (PINFO_FD_VISITED(pinfo)) */ |
493 | 0 | if (req_resp) { |
494 | 0 | if (req_resp->response_frame_num > 0) { |
495 | 0 | proto_item *rep = proto_tree_add_uint(scylla_tree, hf_scylla_request_response_frame, tvb, 0, 0, req_resp->response_frame_num); |
496 | 0 | proto_item_set_generated(rep); |
497 | 0 | } else { |
498 | 0 | expert_add_info(pinfo, request_ti, &ei_scylla_response_missing); |
499 | 0 | } |
500 | 0 | } |
501 | |
|
502 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "Scylla"); |
503 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
504 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, "Request %s", |
505 | 0 | val64_to_str_wmem(pinfo->pool, verb_type, packettypenames, "Unknown (0x%02x)")); |
506 | 0 | return tvb_reported_length(tvb); |
507 | 0 | } |
508 | | |
509 | | static bool |
510 | | response_expected(uint64_t verb_type) |
511 | 0 | { |
512 | 0 | switch (verb_type) { |
513 | 0 | case GOSSIP_DIGEST_SYN: |
514 | 0 | case GOSSIP_DIGEST_ACK: |
515 | 0 | case GOSSIP_DIGEST_ACK2: |
516 | 0 | case GOSSIP_SHUTDOWN: |
517 | 0 | case DEFINITIONS_UPDATE: |
518 | 0 | case MUTATION: |
519 | 0 | case MUTATION_DONE: |
520 | 0 | case MUTATION_FAILED: |
521 | 0 | case HINT_MUTATION: |
522 | 0 | case PAXOS_LEARN: |
523 | 0 | case PAXOS_PRUNE: |
524 | 0 | return false; |
525 | 0 | default: |
526 | 0 | return true; |
527 | 0 | } |
528 | 0 | } |
529 | | |
530 | | static int |
531 | | dissect_scylla_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
532 | 0 | { |
533 | 0 | unsigned offset = 0; |
534 | 0 | conversation_t *conversation; |
535 | 0 | wmem_map_t *conv_map; |
536 | |
|
537 | 0 | proto_item *ti = proto_tree_add_item(tree, proto_scylla, tvb, 0, -1, ENC_NA); |
538 | 0 | proto_tree *scylla_tree = proto_item_add_subtree(ti, ett_scylla); |
539 | |
|
540 | 0 | uint64_t verb_type = LAST; |
541 | 0 | uint32_t len = 0; |
542 | |
|
543 | 0 | if (looks_like_rpc_negotiation(tvb)) { |
544 | 0 | return dissect_scylla_negotiation_pdu(tvb, pinfo, scylla_tree); |
545 | 0 | } |
546 | | |
547 | 0 | if (tvb_reported_length(tvb) >= SCYLLA_HEADER_SIZE) { |
548 | 0 | verb_type = tvb_get_letoh64(tvb, offset + SCYLLA_HEADER_VERB_OFFSET); |
549 | 0 | len = tvb_get_letohl(tvb, offset + SCYLLA_HEADER_LEN_OFFSET); |
550 | 0 | } |
551 | |
|
552 | 0 | conversation = find_or_create_conversation(pinfo); |
553 | 0 | conv_map = (wmem_map_t *)conversation_get_proto_data(conversation, proto_scylla); |
554 | 0 | if (conv_map == NULL) { |
555 | 0 | conv_map = wmem_map_new(wmem_file_scope(), wmem_int64_hash, g_int64_equal); |
556 | 0 | conversation_add_proto_data(conversation, proto_scylla, conv_map); |
557 | 0 | } |
558 | |
|
559 | 0 | if (looks_like_response(verb_type, len)) { |
560 | 0 | void *req_resp; |
561 | 0 | uint64_t msg_id; |
562 | 0 | msg_id = tvb_get_letoh64(tvb, offset + SCYLLA_RESPONSE_MSG_ID_OFFSET); |
563 | 0 | req_resp = wmem_map_lookup(conv_map, &msg_id); |
564 | 0 | return dissect_scylla_response_pdu(tvb, pinfo, scylla_tree, (request_response_t *)req_resp); |
565 | 0 | } |
566 | | |
567 | 0 | uint64_t msg_id = tvb_get_letoh64(tvb, offset + SCYLLA_HEADER_MSG_ID_OFFSET); |
568 | 0 | void *req_resp = NULL; |
569 | |
|
570 | 0 | if (response_expected(verb_type)) { |
571 | 0 | if (!PINFO_FD_VISITED(pinfo)) { |
572 | 0 | uint64_t *key = wmem_new(wmem_file_scope(), uint64_t); |
573 | 0 | request_response_t *val = wmem_new(wmem_file_scope(), request_response_t); |
574 | 0 | *key = msg_id; |
575 | 0 | val->verb_type = verb_type; |
576 | 0 | val->request_frame_num = pinfo->num; |
577 | 0 | wmem_map_insert(conv_map, key, val); |
578 | 0 | } else { |
579 | 0 | req_resp = wmem_map_lookup(conv_map, &msg_id); |
580 | 0 | } |
581 | 0 | } |
582 | |
|
583 | 0 | return dissect_scylla_msg_pdu(tvb, pinfo, scylla_tree, ti, verb_type, len, (request_response_t *)req_resp); |
584 | 0 | } |
585 | | |
586 | | static int |
587 | | dissect_scylla(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) |
588 | 0 | { |
589 | 0 | tcp_dissect_pdus(tvb, pinfo, tree, scylla_desegment, SCYLLA_NEGOTIATION_SIZE, |
590 | 0 | get_scylla_pdu_len, dissect_scylla_pdu, data); |
591 | 0 | return tvb_reported_length(tvb); |
592 | 0 | } |
593 | | |
594 | | void |
595 | | proto_register_scylla(void) |
596 | 16 | { |
597 | 16 | static hf_register_info hf[] = { |
598 | | // RPC header |
599 | 16 | { &hf_scylla_request, { "request", "scylla.request", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, |
600 | 16 | { &hf_scylla_request_response_frame, { "Response frame", "scylla.request.response", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0, NULL, HFILL } }, |
601 | 16 | { &hf_scylla_timeout, { "RPC timeout", "scylla.timeout", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
602 | 16 | { &hf_scylla_verb, { "verb", "scylla.verb", FT_UINT64, BASE_DEC|BASE_VAL64_STRING, VALS64(packettypenames), 0x0, NULL, HFILL } }, |
603 | 16 | { &hf_scylla_msg_id, { "msg id", "scylla.msg_id", FT_UINT64, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
604 | 16 | { &hf_scylla_len, { "packet length", "scylla.len", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
605 | 16 | { &hf_scylla_payload, { "payload", "scylla.payload", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } }, |
606 | 16 | { &hf_scylla_response, { "response", "scylla.response", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, |
607 | 16 | { &hf_scylla_response_size, { "response size", "scylla.response.size", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
608 | 16 | { &hf_scylla_response_request_frame, { "Request frame", "scylla.response.request", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0, NULL, HFILL } }, |
609 | 16 | { &hf_scylla_negotiation_magic, { "negotiation magic sequence", "scylla.negotiation.magic", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, |
610 | 16 | { &hf_scylla_negotiation_size, { "negotiation size", "scylla.negotiation.size", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
611 | 16 | { &hf_scylla_feature_number, { "feature number", "scylla.negotiation.feature.number", FT_UINT32, BASE_DEC, VALS(feature_names), 0x0, NULL, HFILL } }, |
612 | 16 | { &hf_scylla_feature_len, { "feature len", "scylla.negotiation.feature.len", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
613 | 16 | { &hf_scylla_feature_data, { "feature data", "scylla.negotiation.feature.data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } }, |
614 | 16 | { &hf_scylla_connection_id, { "connection ID", "scylla.connection_id", FT_UINT64, BASE_HEX, NULL, 0x0, NULL, HFILL } }, |
615 | 16 | { &hf_scylla_isolation_cookie, { "isolation cookie", "scylla.isolation_cookie", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } }, |
616 | 16 | { &hf_scylla_streaming_len, { "streaming length", "scylla.streaming.length", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
617 | | // mutation verb |
618 | 16 | { &hf_scylla_mut_size1, { "mutation size 1", "scylla.mut.size1", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
619 | 16 | { &hf_scylla_mut_size2, { "mutation size 2", "scylla.mut.size2", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
620 | 16 | { &hf_scylla_mut_table_id, { "mutation table id", "scylla.mut.table_id", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } }, |
621 | 16 | { &hf_scylla_mut_schema_id, { "mutation schema id", "scylla.mut.schema_id", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } }, |
622 | 16 | { &hf_scylla_mut_len_pkeys, { "size of partition keys payload", "scylla.mut.len_pkeys", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
623 | 16 | { &hf_scylla_mut_num_pkeys, { "number of partition keys", "scylla.mut.num_pkeys", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
624 | 16 | { &hf_scylla_mut_len_pkey, { "length of a partition key", "scylla.mut.len_pkey", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
625 | 16 | { &hf_scylla_mut_pkey, { "partition key", "scylla.mut.pkey", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } }, |
626 | | // read_data verb |
627 | 16 | { &hf_scylla_read_data_timeout, { "timeout", "scylla.read_data.timeout", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }, |
628 | 16 | { &hf_scylla_read_data_table_id, { "table ID", "scylla.read_data.table_id", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } }, |
629 | 16 | { &hf_scylla_read_data_schema_version, { "Schema version", "scylla.read_data.schema_version", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } }, |
630 | | |
631 | 16 | }; |
632 | | |
633 | 16 | static ei_register_info ei[] = { |
634 | 16 | { &ei_scylla_response_missing, |
635 | 16 | { "scylla.ei_scylla_response_missing", |
636 | 16 | PI_COMMENTS_GROUP, PI_NOTE, "Response has not arrived yet", EXPFILL }}, |
637 | 16 | }; |
638 | | |
639 | | /* Setup protocol subtree array */ |
640 | 16 | static int *ett[] = { |
641 | 16 | &ett_scylla, |
642 | 16 | &ett_scylla_header, |
643 | 16 | &ett_scylla_response, |
644 | 16 | &ett_scylla_negotiation, |
645 | 16 | &ett_scylla_negotiation_features, |
646 | 16 | &ett_sclla_streaming, |
647 | 16 | &ett_scylla_mut, |
648 | 16 | &ett_scylla_mut_pkey, |
649 | 16 | &ett_scylla_read_data, |
650 | 16 | }; |
651 | | |
652 | 16 | expert_module_t* expert_scylla; |
653 | | |
654 | 16 | proto_scylla = proto_register_protocol("Scylla RPC protocol", "Scylla", "scylla"); |
655 | 16 | module_t* scylla_module = prefs_register_protocol(proto_scylla, NULL); |
656 | 16 | prefs_register_bool_preference(scylla_module, "desegment", |
657 | 16 | "Desegment all Scylla messages spanning multiple TCP segments", |
658 | 16 | "Whether Scylla dissector should desegment all messages spanning multiple TCP segments", |
659 | 16 | &scylla_desegment); |
660 | | |
661 | 16 | proto_register_field_array(proto_scylla, hf, array_length(hf)); |
662 | 16 | proto_register_subtree_array(ett, array_length(ett)); |
663 | 16 | expert_scylla = expert_register_protocol(proto_scylla); |
664 | 16 | expert_register_field_array(expert_scylla, ei, array_length(ei)); |
665 | | |
666 | 16 | scylla_handle = register_dissector("scylla", dissect_scylla, proto_scylla); |
667 | 16 | } |
668 | | |
669 | | void |
670 | | proto_reg_handoff_scylla(void) |
671 | 16 | { |
672 | 16 | dissector_add_uint_with_preference("tcp.port", SCYLLA_PORT, scylla_handle); |
673 | 16 | } |
674 | | |
675 | | /* |
676 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
677 | | * |
678 | | * Local variables: |
679 | | * c-basic-offset: 4 |
680 | | * tab-width: 8 |
681 | | * indent-tabs-mode: nil |
682 | | * End: |
683 | | * |
684 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
685 | | * :indentSize=4:tabSize=8:noTabs=true: |
686 | | */ |