2 * Routines for Nano / RaiBlocks dissection
3 * Copyright 2018, Roland Haenel <roland@haenel.me>
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 * For information about Nano / RaiBlocks, go to http://www.nano.org
18 #include <conversation.h>
19 #include "packet-tcp.h"
20 #include <proto_data.h>
22 #include <epan/packet.h>
23 #include <epan/to_str.h>
24 #include <wsutil/str_util.h>
26 void proto_reg_handoff_nano(void);
27 void proto_register_nano(void);
29 static dissector_handle_t nano_handle
, nano_tcp_handle
;
31 static int proto_nano
;
33 static int hf_nano_magic_number
;
34 static int hf_nano_version_max
;
35 static int hf_nano_version_using
;
36 static int hf_nano_version_min
;
37 static int hf_nano_packet_type
;
38 static int hf_nano_extensions
;
39 static int hf_nano_extensions_block_type
;
40 static int hf_nano_keepalive_peer_ip
;
41 static int hf_nano_keepalive_peer_port
;
43 static int hf_nano_block_hash_previous
;
44 static int hf_nano_block_hash_source
;
45 static int hf_nano_block_signature
;
46 static int hf_nano_block_work
;
47 static int hf_nano_block_destination_account
;
48 static int hf_nano_block_balance
;
49 static int hf_nano_block_account
;
50 static int hf_nano_block_representative_account
;
51 static int hf_nano_block_link
;
53 static int hf_nano_vote_account
;
54 static int hf_nano_vote_signature
;
55 static int hf_nano_vote_sequence
;
57 static int hf_nano_bulk_pull_account
;
58 static int hf_nano_bulk_pull_block_hash_end
;
60 static int hf_nano_frontier_req_account
;
61 static int hf_nano_frontier_req_age
;
62 static int hf_nano_frontier_req_count
;
64 static int hf_nano_bulk_pull_blocks_min_hash
;
65 static int hf_nano_bulk_pull_blocks_max_hash
;
66 static int hf_nano_bulk_pull_blocks_mode
;
67 static int hf_nano_bulk_pull_blocks_max_count
;
69 static int hf_nano_bulk_push_block_type
;
71 static int hf_nano_bulk_pull_block_type
;
73 static int hf_nano_frontier_account
;
74 static int hf_nano_frontier_head_hash
;
77 static int ett_nano_header
;
78 static int ett_nano_extensions
;
79 static int ett_nano_peers
;
80 static int ett_nano_peer_details
[8];
81 static int ett_nano_block
;
82 static int ett_nano_vote
;
83 static int ett_nano_bulk_pull
;
84 static int ett_nano_frontier_req
;
85 static int ett_nano_bulk_pull_blocks
;
86 static int ett_nano_frontier
;
88 #define NANO_PACKET_TYPE_INVALID 0
89 #define NANO_PACKET_TYPE_NOT_A_TYPE 1
90 #define NANO_PACKET_TYPE_KEEPALIVE 2
91 #define NANO_PACKET_TYPE_PUBLISH 3
92 #define NANO_PACKET_TYPE_CONFIRM_REQ 4
93 #define NANO_PACKET_TYPE_CONFIRM_ACK 5
94 #define NANO_PACKET_TYPE_BULK_PULL 6
95 #define NANO_PACKET_TYPE_BULK_PUSH 7
96 #define NANO_PACKET_TYPE_FRONTIER_REQ 8
97 #define NANO_PACKET_TYPE_BULK_PULL_BLOCKS 9
99 static const value_string nano_packet_type_strings
[] = {
100 { NANO_PACKET_TYPE_INVALID
, "Invalid" },
101 { NANO_PACKET_TYPE_NOT_A_TYPE
, "Not A Type" },
102 { NANO_PACKET_TYPE_KEEPALIVE
, "Keepalive" },
103 { NANO_PACKET_TYPE_PUBLISH
, "Publish" },
104 { NANO_PACKET_TYPE_CONFIRM_REQ
, "Confirm Req" },
105 { NANO_PACKET_TYPE_CONFIRM_ACK
, "Confirm Ack" },
106 { NANO_PACKET_TYPE_BULK_PULL
, "Bulk Pull" },
107 { NANO_PACKET_TYPE_BULK_PUSH
, "Bulk Push" },
108 { NANO_PACKET_TYPE_FRONTIER_REQ
, "Frontier Req" },
109 { NANO_PACKET_TYPE_BULK_PULL_BLOCKS
, "Bulk Pull Blocks" },
113 #define NANO_BLOCK_TYPE_INVALID 0
114 #define NANO_BLOCK_TYPE_NOT_A_BLOCK 1
115 #define NANO_BLOCK_TYPE_SEND 2
116 #define NANO_BLOCK_TYPE_RECEIVE 3
117 #define NANO_BLOCK_TYPE_OPEN 4
118 #define NANO_BLOCK_TYPE_CHANGE 5
119 #define NANO_BLOCK_TYPE_STATE 6
121 static const value_string nano_block_type_strings
[] = {
122 { NANO_BLOCK_TYPE_INVALID
, "Invalid" },
123 { NANO_BLOCK_TYPE_NOT_A_BLOCK
, "Not A Block" },
124 { NANO_BLOCK_TYPE_SEND
, "Send" },
125 { NANO_BLOCK_TYPE_RECEIVE
, "Receive" },
126 { NANO_BLOCK_TYPE_OPEN
, "Open" },
127 { NANO_BLOCK_TYPE_CHANGE
, "Change" },
128 { NANO_BLOCK_TYPE_STATE
, "State" },
132 static const string_string nano_magic_numbers
[] = {
133 { "RA", "Nano Test Network" },
134 { "RB", "Nano Beta Network" },
135 { "RC", "Nano Production Network" },
139 #define NANO_BULK_PULL_BLOCKS_MODE_LIST_BLOCKS 0
140 #define NANO_BULK_PULL_BLOCKS_MODE_CHECKSUM_BLOCKS 1
142 static const value_string nano_bulk_pull_blocks_mode_strings
[] = {
143 { NANO_BULK_PULL_BLOCKS_MODE_LIST_BLOCKS
, "List Blocks" },
144 { NANO_BULK_PULL_BLOCKS_MODE_CHECKSUM_BLOCKS
, "Checksum Blocks" },
148 #define NANO_UDP_PORT 7075 /* Not IANA registered */
149 #define NANO_TCP_PORT 7075 /* Not IANA registered */
151 #define NANO_BLOCK_SIZE_SEND (32+32+16+64+8)
152 #define NANO_BLOCK_SIZE_RECEIVE (32+32+64+8)
153 #define NANO_BLOCK_SIZE_OPEN (32+32+32+64+8)
154 #define NANO_BLOCK_SIZE_CHANGE (32+32+64+8)
155 #define NANO_BLOCK_SIZE_STATE (32+32+32+16+32+64+8)
157 // Nano header length, and thus minimum length of any Nano UDP packet (or bootstrap request)
158 #define NANO_HEADER_LENGTH 8
160 // Nano bootstrap session state
161 struct nano_session_state
{
162 int client_packet_type
;
163 uint32_t server_port
;
167 // dissect the inside of a keepalive packet (that is, the neighbor nodes)
168 static int dissect_nano_keepalive(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*nano_tree
, int offset
)
171 proto_tree
*peer_tree
, *peer_entry_tree
;
177 peer_tree
= proto_tree_add_subtree(nano_tree
, tvb
, offset
, 8*(16+2), ett_nano_peers
, NULL
, "Peer List");
180 for (i
= 0; i
< 8; i
++) {
181 peer_entry_tree
= proto_tree_add_subtree(peer_tree
, tvb
, offset
, 18, ett_nano_peer_details
[i
], &ti
, "Peer");
183 tvb_get_ipv6(tvb
, offset
, &ip_addr
);
184 proto_tree_add_item(peer_entry_tree
, hf_nano_keepalive_peer_ip
, tvb
, offset
, 16, ENC_NA
);
187 proto_tree_add_item_ret_uint(peer_entry_tree
, hf_nano_keepalive_peer_port
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
, &port
);
190 if (!memcmp(&ip_addr
, "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16)) {
191 proto_item_append_text(ti
, ": (none)");
192 } else if (!memcmp(&ip_addr
, "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\xff\xff", 12)) {
193 ip_addr_to_str_buf((ws_in4_addr
*)((uint8_t *)&ip_addr
+ 12), buf
, sizeof(buf
));
194 proto_item_append_text(ti
, ": %s:%d", buf
, port
);
197 ip6_to_str_buf(&ip_addr
, buf
, sizeof(buf
));
198 proto_item_append_text(ti
, ": [%s]:%d", buf
, port
);
203 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Keepalive (%d peer%s)", peers
, plurality(peers
, "", "s"));
208 // dissect a receive block
209 static int dissect_nano_receive_block(tvbuff_t
*tvb
, proto_tree
*nano_tree
, int offset
)
211 proto_tree
*block_tree
;
213 block_tree
= proto_tree_add_subtree(nano_tree
, tvb
, offset
, NANO_BLOCK_SIZE_RECEIVE
, ett_nano_block
, NULL
, "Receive Block");
215 proto_tree_add_item(block_tree
, hf_nano_block_hash_previous
, tvb
, offset
, 32, ENC_NA
);
218 proto_tree_add_item(block_tree
, hf_nano_block_hash_source
, tvb
, offset
, 32, ENC_NA
);
221 proto_tree_add_item(block_tree
, hf_nano_block_signature
, tvb
, offset
, 64, ENC_NA
);
224 proto_tree_add_item(block_tree
, hf_nano_block_work
, tvb
, offset
, 8, ENC_NA
);
230 // dissect a send block
231 static int dissect_nano_send_block(tvbuff_t
*tvb
, proto_tree
*nano_tree
, int offset
)
233 proto_tree
*block_tree
;
235 block_tree
= proto_tree_add_subtree(nano_tree
, tvb
, offset
, NANO_BLOCK_SIZE_SEND
, ett_nano_block
, NULL
, "Send Block");
237 proto_tree_add_item(block_tree
, hf_nano_block_hash_previous
, tvb
, offset
, 32, ENC_NA
);
240 proto_tree_add_item(block_tree
, hf_nano_block_destination_account
, tvb
, offset
, 32, ENC_NA
);
243 proto_tree_add_item(block_tree
, hf_nano_block_balance
, tvb
, offset
, 16, ENC_NA
);
246 proto_tree_add_item(block_tree
, hf_nano_block_signature
, tvb
, offset
, 64, ENC_NA
);
249 proto_tree_add_item(block_tree
, hf_nano_block_work
, tvb
, offset
, 8, ENC_NA
);
255 // dissect an open block
256 static int dissect_nano_open_block(tvbuff_t
*tvb
, proto_tree
*nano_tree
, int offset
)
258 proto_tree
*block_tree
;
260 block_tree
= proto_tree_add_subtree(nano_tree
, tvb
, offset
, NANO_BLOCK_SIZE_OPEN
, ett_nano_block
, NULL
, "Open Block");
262 proto_tree_add_item(block_tree
, hf_nano_block_hash_source
, tvb
, offset
, 32, ENC_NA
);
265 proto_tree_add_item(block_tree
, hf_nano_block_representative_account
, tvb
, offset
, 32, ENC_NA
);
268 proto_tree_add_item(block_tree
, hf_nano_block_account
, tvb
, offset
, 32, ENC_NA
);
271 proto_tree_add_item(block_tree
, hf_nano_block_signature
, tvb
, offset
, 64, ENC_NA
);
274 proto_tree_add_item(block_tree
, hf_nano_block_work
, tvb
, offset
, 8, ENC_NA
);
280 // dissect an change block
281 static int dissect_nano_change_block(tvbuff_t
*tvb
, proto_tree
*nano_tree
, int offset
)
283 proto_tree
*block_tree
;
285 block_tree
= proto_tree_add_subtree(nano_tree
, tvb
, offset
, NANO_BLOCK_SIZE_CHANGE
, ett_nano_block
, NULL
, "Change Block");
287 proto_tree_add_item(block_tree
, hf_nano_block_hash_previous
, tvb
, offset
, 32, ENC_NA
);
290 proto_tree_add_item(block_tree
, hf_nano_block_representative_account
, tvb
, offset
, 32, ENC_NA
);
293 proto_tree_add_item(block_tree
, hf_nano_block_signature
, tvb
, offset
, 64, ENC_NA
);
296 proto_tree_add_item(block_tree
, hf_nano_block_work
, tvb
, offset
, 8, ENC_NA
);
302 // dissect a state block
303 static int dissect_nano_state(tvbuff_t
*tvb
, proto_tree
*nano_tree
, int offset
)
305 proto_tree
*block_tree
;
307 block_tree
= proto_tree_add_subtree(nano_tree
, tvb
, offset
, NANO_BLOCK_SIZE_STATE
, ett_nano_block
, NULL
, "State Block");
309 proto_tree_add_item(block_tree
, hf_nano_block_account
, tvb
, offset
, 32, ENC_NA
);
312 proto_tree_add_item(block_tree
, hf_nano_block_hash_previous
, tvb
, offset
, 32, ENC_NA
);
315 proto_tree_add_item(block_tree
, hf_nano_block_representative_account
, tvb
, offset
, 32, ENC_NA
);
318 proto_tree_add_item(block_tree
, hf_nano_block_balance
, tvb
, offset
, 16, ENC_NA
);
321 proto_tree_add_item(block_tree
, hf_nano_block_link
, tvb
, offset
, 32, ENC_NA
);
324 proto_tree_add_item(block_tree
, hf_nano_block_signature
, tvb
, offset
, 64, ENC_NA
);
327 proto_tree_add_item(block_tree
, hf_nano_block_work
, tvb
, offset
, 8, ENC_NA
);
334 static int dissect_nano_vote(tvbuff_t
*tvb
, proto_tree
*nano_tree
, int offset
)
336 proto_tree
*vote_tree
;
338 vote_tree
= proto_tree_add_subtree(nano_tree
, tvb
, offset
, 32+64+8, ett_nano_block
, NULL
, "Vote");
340 proto_tree_add_item(vote_tree
, hf_nano_vote_account
, tvb
, offset
, 32, ENC_NA
);
343 proto_tree_add_item(vote_tree
, hf_nano_vote_signature
, tvb
, offset
, 64, ENC_NA
);
346 proto_tree_add_item(vote_tree
, hf_nano_vote_sequence
, tvb
, offset
, 8, ENC_LITTLE_ENDIAN
);
352 // dissect a Nano protocol header, fills in the values
353 // for nano_packet_type, nano_block_type
354 static int dissect_nano_header(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*nano_tree
, int offset
, unsigned *nano_packet_type
, uint64_t *extensions
)
356 proto_tree
*header_tree
;
357 char *nano_magic_number
;
358 static int * const nano_extensions
[] = {
359 &hf_nano_extensions_block_type
,
363 header_tree
= proto_tree_add_subtree(nano_tree
, tvb
, offset
, NANO_HEADER_LENGTH
, ett_nano_header
, NULL
, "Nano Protocol Header");
365 nano_magic_number
= tvb_get_string_enc(pinfo
->pool
, tvb
, offset
, 2, ENC_ASCII
);
366 proto_tree_add_string_format_value(header_tree
, hf_nano_magic_number
, tvb
, 0,
367 2, nano_magic_number
, "%s (%s)", str_to_str(nano_magic_number
, nano_magic_numbers
, "Unknown"), nano_magic_number
);
370 proto_tree_add_item(header_tree
, hf_nano_version_max
, tvb
, offset
, 1, ENC_NA
);
373 proto_tree_add_item(header_tree
, hf_nano_version_using
, tvb
, offset
, 1, ENC_NA
);
376 proto_tree_add_item(header_tree
, hf_nano_version_min
, tvb
, offset
, 1, ENC_NA
);
379 proto_tree_add_item_ret_uint(header_tree
, hf_nano_packet_type
, tvb
, offset
, 1, ENC_NA
, nano_packet_type
);
382 proto_tree_add_bitmask_ret_uint64(header_tree
, tvb
, offset
, hf_nano_extensions
, ett_nano_extensions
, nano_extensions
, ENC_LITTLE_ENDIAN
, extensions
);
388 // dissect a Nano packet (UDP)
389 static int dissect_nano(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
392 proto_tree
*nano_tree
;
393 unsigned nano_packet_type
, nano_block_type
, offset
;
396 /* Check that the packet is long enough for it to belong to us. */
397 if (tvb_reported_length(tvb
) < NANO_HEADER_LENGTH
)
400 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "Nano");
401 col_clear(pinfo
->cinfo
, COL_INFO
);
403 ti
= proto_tree_add_item(tree
, proto_nano
, tvb
, 0, -1, ENC_NA
);
404 nano_tree
= proto_item_add_subtree(ti
, ett_nano
);
406 offset
= dissect_nano_header(tvb
, pinfo
, nano_tree
, 0, &nano_packet_type
, &extensions
);
408 // call specific dissectors for specific packet types
409 switch (nano_packet_type
) {
410 case NANO_PACKET_TYPE_KEEPALIVE
:
411 return dissect_nano_keepalive(tvb
, pinfo
, nano_tree
, offset
);
413 case NANO_PACKET_TYPE_PUBLISH
:
414 case NANO_PACKET_TYPE_CONFIRM_REQ
:
415 case NANO_PACKET_TYPE_CONFIRM_ACK
:
417 // set the INFO header with more information
418 nano_block_type
= (unsigned)((extensions
>> 8) & 0xF);
419 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "%s (%s)",
420 val_to_str_const(nano_packet_type
, VALS(nano_packet_type_strings
), " "),
421 val_to_str(nano_block_type
, VALS(nano_block_type_strings
), "Unknown (%d)"));
423 // if it's a Confirm Ack packet, we first have a vote
424 if (nano_packet_type
== NANO_PACKET_TYPE_CONFIRM_ACK
) {
425 offset
= dissect_nano_vote(tvb
, nano_tree
, offset
);
428 // dissect the actual block
429 switch (nano_block_type
) {
430 case NANO_BLOCK_TYPE_RECEIVE
:
431 dissect_nano_receive_block(tvb
, nano_tree
, offset
);
433 case NANO_BLOCK_TYPE_SEND
:
434 dissect_nano_send_block(tvb
, nano_tree
, offset
);
436 case NANO_BLOCK_TYPE_OPEN
:
437 dissect_nano_open_block(tvb
, nano_tree
, offset
);
439 case NANO_BLOCK_TYPE_CHANGE
:
440 dissect_nano_change_block(tvb
, nano_tree
, offset
);
442 case NANO_BLOCK_TYPE_STATE
:
443 dissect_nano_state(tvb
, nano_tree
, offset
);
449 col_add_str(pinfo
->cinfo
, COL_INFO
,
450 val_to_str(nano_packet_type
, VALS(nano_packet_type_strings
), "Unknown (%d)"));
453 return tvb_captured_length(tvb
);
456 // determine the length of a nano bootstrap message (client)
457 static unsigned get_nano_tcp_client_message_len(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset
, void *data _U_
)
459 int nano_packet_type
, nano_block_type
;
460 struct nano_session_state
*session_state
;
462 session_state
= (struct nano_session_state
*)data
;
463 if (session_state
->client_packet_type
== NANO_PACKET_TYPE_BULK_PUSH
) {
464 // we're in the middle of a bulk push, so we expect a block type (uint8) and a block
466 nano_block_type
= tvb_get_uint8(tvb
, offset
);
467 switch (nano_block_type
) {
468 case NANO_BLOCK_TYPE_NOT_A_BLOCK
:
470 case NANO_BLOCK_TYPE_SEND
:
471 return 1 + NANO_BLOCK_SIZE_SEND
;
472 case NANO_BLOCK_TYPE_RECEIVE
:
473 return 1 + NANO_BLOCK_SIZE_RECEIVE
;
474 case NANO_BLOCK_TYPE_OPEN
:
475 return 1 + NANO_BLOCK_SIZE_OPEN
;
476 case NANO_BLOCK_TYPE_CHANGE
:
477 return 1 + NANO_BLOCK_SIZE_CHANGE
;
478 case NANO_BLOCK_TYPE_STATE
:
479 return 1 + NANO_BLOCK_SIZE_STATE
;
482 return tvb_captured_length(tvb
) - offset
;
486 // we expect a client command, this starts with a full Nano header
487 if (tvb_captured_length(tvb
) - offset
< NANO_HEADER_LENGTH
) {
491 nano_packet_type
= tvb_get_uint8(tvb
, offset
+ 5);
493 switch (nano_packet_type
) {
494 case NANO_PACKET_TYPE_BULK_PULL
:
495 return NANO_HEADER_LENGTH
+ 32 + 32;
496 case NANO_PACKET_TYPE_BULK_PUSH
:
497 return NANO_HEADER_LENGTH
;
498 case NANO_PACKET_TYPE_FRONTIER_REQ
:
499 return NANO_HEADER_LENGTH
+ 32 + 4 + 4;
500 case NANO_PACKET_TYPE_BULK_PULL_BLOCKS
:
501 return NANO_HEADER_LENGTH
+ 32 + 32 + 1 + 4;
504 return tvb_captured_length(tvb
) - offset
;
507 // dissect a bulk pull request
508 static int dissect_nano_bulk_pull(tvbuff_t
*tvb
, proto_tree
*nano_tree
, int offset
)
510 proto_tree
*vote_tree
;
512 vote_tree
= proto_tree_add_subtree(nano_tree
, tvb
, offset
, 32+32, ett_nano_bulk_pull
, NULL
, "Bulk Pull");
514 proto_tree_add_item(vote_tree
, hf_nano_bulk_pull_account
, tvb
, offset
, 32, ENC_NA
);
517 proto_tree_add_item(vote_tree
, hf_nano_bulk_pull_block_hash_end
, tvb
, offset
, 32, ENC_NA
);
523 // dissect a frontier request
524 static int dissect_nano_frontier_req(tvbuff_t
*tvb
, proto_tree
*nano_tree
, int offset
)
526 proto_tree
*vote_tree
;
528 vote_tree
= proto_tree_add_subtree(nano_tree
, tvb
, offset
, 32+4+4, ett_nano_frontier_req
, NULL
, "Frontier Request");
530 proto_tree_add_item(vote_tree
, hf_nano_frontier_req_account
, tvb
, offset
, 32, ENC_NA
);
533 proto_tree_add_item(vote_tree
, hf_nano_frontier_req_age
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
536 proto_tree_add_item(vote_tree
, hf_nano_frontier_req_count
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
542 // dissect a bulk pull blocks request
543 static int dissect_nano_bulk_pull_blocks(tvbuff_t
*tvb
, proto_tree
*nano_tree
, int offset
)
545 proto_tree
*vote_tree
;
547 vote_tree
= proto_tree_add_subtree(nano_tree
, tvb
, offset
, 32+4+4, ett_nano_frontier_req
, NULL
, "Bulk Pull Blocks");
549 proto_tree_add_item(vote_tree
, hf_nano_bulk_pull_blocks_min_hash
, tvb
, offset
, 32, ENC_NA
);
552 proto_tree_add_item(vote_tree
, hf_nano_bulk_pull_blocks_max_hash
, tvb
, offset
, 32, ENC_NA
);
555 proto_tree_add_item(nano_tree
, hf_nano_bulk_pull_blocks_mode
, tvb
, offset
, 1, ENC_NA
);
558 proto_tree_add_item(vote_tree
, hf_nano_bulk_pull_blocks_max_count
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
564 // dissect a single nano bootstrap message (client)
565 static int dissect_nano_tcp_client_message(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree _U_
, void *data _U_
)
567 int offset
, nano_packet_type
, nano_block_type
;
569 struct nano_session_state
*session_state
;
571 session_state
= (struct nano_session_state
*)data
;
573 if (session_state
->client_packet_type
== NANO_PACKET_TYPE_BULK_PUSH
) {
574 // we're within a bulk push
575 col_set_str(pinfo
->cinfo
, COL_INFO
, "Bulk Push ");
576 proto_tree_add_item_ret_uint(tree
, hf_nano_bulk_push_block_type
, tvb
, 0, 1, ENC_NA
, &nano_block_type
);
577 switch (nano_block_type
) {
578 case NANO_BLOCK_TYPE_NOT_A_BLOCK
:
579 session_state
->client_packet_type
= NANO_PACKET_TYPE_INVALID
;
581 case NANO_BLOCK_TYPE_SEND
:
582 dissect_nano_send_block(tvb
, tree
, 1);
584 case NANO_BLOCK_TYPE_RECEIVE
:
585 dissect_nano_receive_block(tvb
, tree
, 1);
587 case NANO_BLOCK_TYPE_OPEN
:
588 dissect_nano_open_block(tvb
, tree
, 1);
590 case NANO_BLOCK_TYPE_CHANGE
:
591 dissect_nano_change_block(tvb
, tree
, 1);
593 case NANO_BLOCK_TYPE_STATE
:
594 dissect_nano_state(tvb
, tree
, 1);
597 return tvb_captured_length(tvb
);
600 // a bootstrap client command starts with a Nano header
601 offset
= dissect_nano_header(tvb
, pinfo
, tree
, 0, &nano_packet_type
, &extensions
);
602 session_state
->client_packet_type
= nano_packet_type
;
604 switch (nano_packet_type
) {
605 case NANO_PACKET_TYPE_BULK_PULL
:
606 col_set_str(pinfo
->cinfo
, COL_INFO
, "Bulk Pull Request ");
607 dissect_nano_bulk_pull(tvb
, tree
, offset
);
609 case NANO_PACKET_TYPE_BULK_PUSH
:
610 col_set_str(pinfo
->cinfo
, COL_INFO
, "Bulk Push Request ");
612 case NANO_PACKET_TYPE_FRONTIER_REQ
:
613 col_set_str(pinfo
->cinfo
, COL_INFO
, "Frontier Request ");
614 dissect_nano_frontier_req(tvb
, tree
, offset
);
616 case NANO_PACKET_TYPE_BULK_PULL_BLOCKS
:
617 col_set_str(pinfo
->cinfo
, COL_INFO
, "Bulk Pull Blocks Request ");
618 dissect_nano_bulk_pull_blocks(tvb
, tree
, offset
);
622 return tvb_captured_length(tvb
);
625 // determine the length of a nano bootstrap message (server)
626 static unsigned get_nano_tcp_server_message_len(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset
, void *data _U_
)
629 struct nano_session_state
*session_state
;
631 session_state
= (struct nano_session_state
*)data
;
633 if (session_state
->client_packet_type
== NANO_PACKET_TYPE_BULK_PULL
||
634 session_state
->client_packet_type
== NANO_PACKET_TYPE_BULK_PULL_BLOCKS
) {
635 // we're in response to a bulk pull (blocks), so we expect a block type (uint8) and a block
637 nano_block_type
= tvb_get_uint8(tvb
, offset
);
638 switch (nano_block_type
) {
639 case NANO_BLOCK_TYPE_NOT_A_BLOCK
:
641 case NANO_BLOCK_TYPE_SEND
:
642 return 1 + NANO_BLOCK_SIZE_SEND
;
643 case NANO_BLOCK_TYPE_RECEIVE
:
644 return 1 + NANO_BLOCK_SIZE_RECEIVE
;
645 case NANO_BLOCK_TYPE_OPEN
:
646 return 1 + NANO_BLOCK_SIZE_OPEN
;
647 case NANO_BLOCK_TYPE_CHANGE
:
648 return 1 + NANO_BLOCK_SIZE_CHANGE
;
649 case NANO_BLOCK_TYPE_STATE
:
650 return 1 + NANO_BLOCK_SIZE_STATE
;
653 return tvb_captured_length(tvb
) - offset
;
657 if (session_state
->client_packet_type
== NANO_PACKET_TYPE_FRONTIER_REQ
) {
661 return tvb_captured_length(tvb
) - offset
;
664 // dissect a frontier response entry
665 static int dissect_nano_frontier(tvbuff_t
*tvb
, proto_tree
*nano_tree
, int offset
)
667 proto_tree
*frontier_tree
;
669 frontier_tree
= proto_tree_add_subtree(nano_tree
, tvb
, offset
, 32+32, ett_nano_frontier
, NULL
, "Frontier");
671 proto_tree_add_item(frontier_tree
, hf_nano_frontier_account
, tvb
, offset
, 32, ENC_NA
);
674 proto_tree_add_item(frontier_tree
, hf_nano_frontier_head_hash
, tvb
, offset
, 32, ENC_NA
);
680 // dissect a single nano bootstrap message (server)
681 static int dissect_nano_tcp_server_message(tvbuff_t
*tvb
, packet_info
*pinfo _U_
, proto_tree
*tree _U_
, void *data _U_
)
684 struct nano_session_state
*session_state
;
686 session_state
= (struct nano_session_state
*)data
;
688 if (session_state
->client_packet_type
== NANO_PACKET_TYPE_BULK_PULL
||
689 session_state
->client_packet_type
== NANO_PACKET_TYPE_BULK_PULL_BLOCKS
) {
691 // we're within a bulk pull (blocks)
692 col_set_str(pinfo
->cinfo
, COL_INFO
, session_state
->client_packet_type
== NANO_PACKET_TYPE_BULK_PULL
? "Bulk Pull Response " : "Bulk Pull Blocks Response ");
694 proto_tree_add_item_ret_uint(tree
, hf_nano_bulk_pull_block_type
, tvb
, 0, 1, ENC_NA
, &nano_block_type
);
695 switch (nano_block_type
) {
696 case NANO_BLOCK_TYPE_NOT_A_BLOCK
:
697 session_state
->client_packet_type
= NANO_PACKET_TYPE_INVALID
;
699 case NANO_BLOCK_TYPE_SEND
:
700 dissect_nano_send_block(tvb
, tree
, 1);
702 case NANO_BLOCK_TYPE_RECEIVE
:
703 dissect_nano_receive_block(tvb
, tree
, 1);
705 case NANO_BLOCK_TYPE_OPEN
:
706 dissect_nano_open_block(tvb
, tree
, 1);
708 case NANO_BLOCK_TYPE_CHANGE
:
709 dissect_nano_change_block(tvb
, tree
, 1);
711 case NANO_BLOCK_TYPE_STATE
:
712 dissect_nano_state(tvb
, tree
, 1);
715 return tvb_captured_length(tvb
);
718 if (session_state
->client_packet_type
== NANO_PACKET_TYPE_FRONTIER_REQ
) {
719 col_set_str(pinfo
->cinfo
, COL_INFO
, "Frontier Response ");
720 dissect_nano_frontier(tvb
, tree
, 0);
723 return tvb_captured_length(tvb
);
726 // dissect a Nano bootstrap packet (TCP)
727 static int dissect_nano_tcp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
731 proto_tree
*nano_tree
;
732 conversation_t
*conversation
;
733 struct nano_session_state
*session_state
, *packet_session_state
;
735 // try to find this conversation
736 if ((conversation
= find_conversation_pinfo(pinfo
, 0)) == NULL
) {
737 // create new conversation
738 conversation
= conversation_new(pinfo
->num
, &pinfo
->src
, &pinfo
->dst
, conversation_pt_to_conversation_type(pinfo
->ptype
),
739 pinfo
->srcport
, pinfo
->destport
, 0);
742 // try to find session state
743 session_state
= (struct nano_session_state
*)conversation_get_proto_data(conversation
, proto_nano
);
744 if (!session_state
) {
745 // create new session state
746 session_state
= wmem_new0(wmem_file_scope(), struct nano_session_state
);
747 session_state
->client_packet_type
= NANO_PACKET_TYPE_INVALID
;
748 session_state
->server_port
= pinfo
->match_uint
;
749 conversation_add_proto_data(conversation
, proto_nano
, session_state
);
752 // check if we have a session state associated with the packet (start state for this packet)
753 packet_session_state
= (struct nano_session_state
*)p_get_proto_data(wmem_file_scope(), pinfo
, proto_nano
, 0);
754 if (!packet_session_state
) {
755 // this packet does not have a stored session state, get it from the conversation
756 packet_session_state
= wmem_new0(wmem_file_scope(), struct nano_session_state
);
757 memcpy(packet_session_state
, session_state
, sizeof(struct nano_session_state
));
758 p_add_proto_data(wmem_file_scope(), pinfo
, proto_nano
, 0, packet_session_state
);
760 // this packet has a stored session state, take this as a starting point
761 memcpy(session_state
, packet_session_state
, sizeof(struct nano_session_state
));
764 // set some columns to meaningful defaults
765 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "Nano Bootstrap");
766 col_clear(pinfo
->cinfo
, COL_INFO
);
768 // add Nano protocol tree
769 ti
= proto_tree_add_item(tree
, proto_nano
, tvb
, 0, -1, ENC_NA
);
770 nano_tree
= proto_item_add_subtree(ti
, ett_nano
);
772 // is this a bootstrap client or server?
773 is_client
= pinfo
->destport
== session_state
->server_port
;
776 // Nano bootstrap client
777 tcp_dissect_pdus(tvb
, pinfo
, nano_tree
, true, 1, get_nano_tcp_client_message_len
, dissect_nano_tcp_client_message
, session_state
);
780 // Nano bootstrap server
781 tcp_dissect_pdus(tvb
, pinfo
, nano_tree
, true, 1, get_nano_tcp_server_message_len
, dissect_nano_tcp_server_message
, session_state
);
784 return tvb_captured_length(tvb
);
787 /* Heuristics test */
788 static bool test_nano(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset _U_
, void *data _U_
)
790 // if it's not a complete header length, it's not Nano.
791 if (tvb_captured_length(tvb
) < NANO_HEADER_LENGTH
)
794 // first byte must be 'R', second byte 'A' or 'B' or 'C'
795 if (tvb_get_uint8(tvb
, 0) != (uint8_t) 'R')
798 char network
= (char) tvb_get_uint8(tvb
, 1);
799 if (network
!= 'A' && network
!= 'B' && network
!= 'C')
802 uint8_t version_max
= tvb_get_uint8(tvb
, 2);
803 uint8_t version_using
= tvb_get_uint8(tvb
, 3);
804 uint8_t version_min
= tvb_get_uint8(tvb
, 4);
805 if (version_max
> 30 || version_max
< version_using
|| version_using
< version_min
)
808 uint8_t ptype
= tvb_get_uint8(tvb
, 5);
815 static bool dissect_nano_heur_tcp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
817 conversation_t
*conversation
;
818 struct nano_session_state
*session_state
;
820 if (!test_nano(pinfo
, tvb
, 0, data
))
823 conversation
= find_or_create_conversation(pinfo
);
824 conversation_set_dissector(conversation
, nano_tcp_handle
);
826 // try to find session state
827 session_state
= (struct nano_session_state
*)conversation_get_proto_data(conversation
, proto_nano
);
828 if (!session_state
) {
829 // create new session state
830 session_state
= wmem_new0(wmem_file_scope(), struct nano_session_state
);
831 session_state
->client_packet_type
= NANO_PACKET_TYPE_INVALID
;
832 session_state
->server_port
= pinfo
->destport
;
833 conversation_add_proto_data(conversation
, proto_nano
, session_state
);
836 dissect_nano_tcp(tvb
, pinfo
, tree
, data
);
841 static bool dissect_nano_heur_udp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
843 conversation_t
*conversation
;
845 if (!test_nano(pinfo
, tvb
, 0, data
))
848 conversation
= find_or_create_conversation(pinfo
);
849 conversation_set_dissector(conversation
, nano_handle
);
851 dissect_nano(tvb
, pinfo
, tree
, data
);
856 void proto_register_nano(void)
858 static hf_register_info hf
[] = {
859 { &hf_nano_magic_number
,
860 { "Magic Number", "nano.magic_number",
861 FT_STRING
, BASE_NONE
, NULL
, 0x00,
862 "Nano Protocol Magic Number", HFILL
}
864 { &hf_nano_version_max
,
865 { "Maximum Version", "nano.version_max",
866 FT_UINT8
, BASE_DEC_HEX
, NULL
, 0x00,
867 "Maximum Supported Protocol Version", HFILL
}
869 { &hf_nano_version_using
,
870 { "Using Version", "nano.version_using",
871 FT_UINT8
, BASE_DEC_HEX
, NULL
, 0x00,
872 "Used Protocol Version", HFILL
}
874 { &hf_nano_version_min
,
875 { "Minimum Version", "nano.version_min",
876 FT_UINT8
, BASE_DEC_HEX
, NULL
, 0x00,
877 "Minimum Supported Protocol Version", HFILL
}
879 { &hf_nano_packet_type
,
880 { "Packet Type", "nano.packet_type",
881 FT_UINT8
, BASE_DEC_HEX
, VALS(nano_packet_type_strings
), 0x00,
884 { &hf_nano_extensions
,
885 { "Extensions Field", "nano.extensions",
886 FT_UINT16
, BASE_HEX
, NULL
, 0x00,
889 { &hf_nano_extensions_block_type
,
890 { "Block Type", "nano.extensions.block_type",
891 FT_UINT16
, BASE_HEX
, VALS(nano_block_type_strings
), 0x0f00,
894 { &hf_nano_keepalive_peer_ip
,
895 { "Peer IP Address", "nano.keepalive.peer_ip",
896 FT_IPv6
, BASE_NONE
, NULL
, 0x00,
899 { &hf_nano_keepalive_peer_port
,
900 { "Peer Port", "nano.keepalive.peer_port",
901 FT_UINT16
, BASE_DEC
, NULL
, 0x00,
904 { &hf_nano_block_hash_previous
,
905 { "Previous Block Hash", "nano.block.hash_previous",
906 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
909 { &hf_nano_block_hash_source
,
910 { "Source Block Hash", "nano.block.hash_source",
911 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
914 { &hf_nano_block_signature
,
915 { "Signature", "nano.block.signature",
916 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
919 { &hf_nano_block_work
,
920 { "Work", "nano.block.work",
921 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
924 { &hf_nano_block_destination_account
,
925 { "Destination Account", "nano.block.destination_account",
926 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
929 { &hf_nano_block_balance
,
930 { "Balance", "nano.block.balance",
931 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
934 { &hf_nano_block_account
,
935 { "Account", "nano.block.account",
936 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
939 { &hf_nano_block_representative_account
,
940 { "Representative Account", "nano.block.representative_account",
941 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
944 { &hf_nano_block_link
,
945 { "Link", "nano.block.link",
946 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
949 { &hf_nano_vote_account
,
950 { "Account", "nano.vote.account",
951 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
954 { &hf_nano_vote_signature
,
955 { "Signature", "nano.vote.signature",
956 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
959 { &hf_nano_vote_sequence
,
960 { "Sequence", "nano.vote.sequence",
961 FT_UINT64
, BASE_DEC_HEX
, NULL
, 0x00,
964 { &hf_nano_bulk_pull_account
,
965 { "Account", "nano.bulk_pull.account",
966 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
969 { &hf_nano_bulk_pull_block_hash_end
,
970 { "End Block Hash", "nano.bulk_pull_block.hash_end",
971 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
974 { &hf_nano_frontier_req_account
,
975 { "Account", "nano.frontier_req.account",
976 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
979 { &hf_nano_frontier_req_age
,
980 { "Age", "nano.frontier_req.age",
981 FT_UINT32
, BASE_HEX_DEC
, NULL
, 0x00,
984 { &hf_nano_frontier_req_count
,
985 { "Count", "nano.frontier_req.count",
986 FT_UINT32
, BASE_HEX_DEC
, NULL
, 0x00,
989 { &hf_nano_bulk_pull_blocks_min_hash
,
990 { "Min Block Hash", "nano.bulk_pull_blocks.min_hash",
991 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
994 { &hf_nano_bulk_pull_blocks_max_hash
,
995 { "Max Block Hash", "nano.bulk_pull_blocks.max_hash",
996 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
999 { &hf_nano_bulk_pull_blocks_mode
,
1000 { "Mode", "nano.bulk_pull_blocks.mode",
1001 FT_UINT8
, BASE_DEC_HEX
, VALS(nano_bulk_pull_blocks_mode_strings
), 0x00,
1004 { &hf_nano_bulk_pull_blocks_max_count
,
1005 { "Max Count", "nano.bulk_pull_blocks.max_count",
1006 FT_UINT32
, BASE_HEX_DEC
, NULL
, 0x00,
1009 { &hf_nano_bulk_push_block_type
,
1010 { "Block Type", "nano.bulk_push.block_type",
1011 FT_UINT8
, BASE_HEX
, VALS(nano_block_type_strings
), 0x00,
1014 { &hf_nano_bulk_pull_block_type
,
1015 { "Block Type", "nano.bulk_pull.block_type",
1016 FT_UINT8
, BASE_HEX
, VALS(nano_block_type_strings
), 0x00,
1019 { &hf_nano_frontier_account
,
1020 { "Account", "nano.frontier.account",
1021 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
1024 { &hf_nano_frontier_head_hash
,
1025 { "Head Hash", "nano.frontier.head_hash",
1026 FT_BYTES
, BASE_NONE
, NULL
, 0x00,
1031 static int *ett
[] = {
1034 &ett_nano_extensions
,
1036 &ett_nano_peer_details
[0],
1037 &ett_nano_peer_details
[1],
1038 &ett_nano_peer_details
[2],
1039 &ett_nano_peer_details
[3],
1040 &ett_nano_peer_details
[4],
1041 &ett_nano_peer_details
[5],
1042 &ett_nano_peer_details
[6],
1043 &ett_nano_peer_details
[7],
1046 &ett_nano_bulk_pull
,
1047 &ett_nano_frontier_req
,
1048 &ett_nano_bulk_pull_blocks
,
1052 proto_nano
= proto_register_protocol("Nano Cryptocurrency Protocol", "Nano", "nano");
1054 proto_register_field_array(proto_nano
, hf
, array_length(hf
));
1055 proto_register_subtree_array(ett
, array_length(ett
));
1058 void proto_reg_handoff_nano(void)
1060 nano_handle
= register_dissector("nano", dissect_nano
, proto_nano
);
1061 dissector_add_uint_with_preference("udp.port", NANO_UDP_PORT
, nano_handle
);
1062 heur_dissector_add("udp", dissect_nano_heur_udp
, "Nano UDP Heuristics", "nano-udp", proto_nano
, HEURISTIC_DISABLE
);
1064 nano_tcp_handle
= register_dissector("nano-over-tcp", dissect_nano_tcp
, proto_nano
);
1065 dissector_add_uint_with_preference("tcp.port", NANO_TCP_PORT
, nano_tcp_handle
);
1066 heur_dissector_add("tcp", dissect_nano_heur_tcp
, "Nano TCP Heuristics", "nano-tcp", proto_nano
, HEURISTIC_DISABLE
);
1070 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1075 * indent-tabs-mode: nil
1078 * vi: set shiftwidth=4 tabstop=8 expandtab:
1079 * :indentSize=4:tabSize=8:noTabs=true: