Coverage Report

Created: 2026-09-28 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
 */