Line data Source code
1 : #include "fd_gossip_private.h"
2 : #include "../../ballet/txn/fd_compact_u16.h"
3 :
4 : /* Adapted from fd_txn_parse.c */
5 : #define CHECK_INIT( payload, payload_sz, offset ) \
6 0 : uchar const * _payload = (payload); \
7 0 : ulong const _payload_sz = (payload_sz); \
8 0 : ulong const _offset = (offset); \
9 0 : ulong _i = (offset); \
10 0 : (void) _payload; \
11 0 : (void) _offset; \
12 :
13 0 : #define CHECK( cond ) do { \
14 0 : if( FD_UNLIKELY( !(cond) ) ) { \
15 0 : FD_LOG_WARNING(( "Gossip message parse error at offset %lu, size %lu: %s", _i, _payload_sz, #cond )); \
16 0 : return 0; \
17 0 : } \
18 0 : } while( 0 )
19 :
20 0 : #define CHECK_LEFT( n ) CHECK( (n)<=(_payload_sz-_i) )
21 :
22 0 : #define INC( n ) (_i += (ulong)(n))
23 :
24 0 : #define CHECKED_INC( n ) do { \
25 0 : CHECK_LEFT( n ); \
26 0 : INC( n ); \
27 0 : } while( 0 )
28 :
29 : #define READ_CHECKED_COMPACT_U16( out_sz, var_name, where ) \
30 0 : do { \
31 0 : ulong _where = (where); \
32 0 : ulong _out_sz = fd_cu16_dec_sz( _payload+_where, _payload_sz-_where ); \
33 0 : CHECK( _out_sz ); \
34 0 : (var_name) = fd_cu16_dec_fixed( _payload+_where, _out_sz ); \
35 0 : (out_sz) = _out_sz; \
36 0 : } while( 0 )
37 0 : #define CUR_OFFSET ((ushort)_i)
38 0 : #define CURSOR (_payload+_i)
39 0 : #define BYTES_CONSUMED (_i-_offset)
40 : #define BYTES_REMAINING (_payload_sz-_i)
41 :
42 : static ulong
43 : decode_u64_varint( uchar const * payload,
44 : ulong payload_sz,
45 : ulong start_offset,
46 0 : ulong * out_value ) {
47 0 : CHECK_INIT( payload, payload_sz, start_offset );
48 0 : ulong value = 0UL;
49 0 : ulong shift = 0U;
50 0 : while( FD_LIKELY( _i < _payload_sz ) ) {
51 0 : uchar byte = FD_LOAD( uchar, CURSOR ); INC( 1U );
52 0 : value |= (ulong)(byte & 0x7F) << shift;
53 0 : if( !(byte & 0x80) ) break;
54 0 : shift += 7U;
55 0 : if( FD_UNLIKELY( shift >= 64U ) ) return 0;
56 0 : }
57 0 : *out_value = value;
58 0 : return BYTES_CONSUMED;
59 0 : }
60 :
61 : static ulong
62 : fd_gossip_msg_crds_legacy_contact_info_parse( fd_gossip_view_crds_value_t * crds_val,
63 : uchar const * payload,
64 : ulong payload_sz,
65 0 : ulong start_offset ) {
66 0 : CHECK_INIT( payload, payload_sz, start_offset );
67 : /* https://github.com/anza-xyz/agave/blob/540d5bc56cd44e3cc61b179bd52e9a782a2c99e4/gossip/src/legacy_contact_info.rs#L13 */
68 0 : CHECK_LEFT( 32U ); crds_val->pubkey_off = CUR_OFFSET ; INC( 32U );
69 0 : for( ulong i=0UL; i<10; i++ ) {
70 0 : CHECK_LEFT( 4U ); uint is_ip6 = FD_LOAD( uint, CURSOR ) ; INC( 4U );
71 0 : if( !is_ip6 ){
72 0 : CHECKED_INC( 4U+2U ); /* ip4 + port */
73 0 : } else {
74 0 : CHECKED_INC( 16U+2U+4U+4U ); /* ip6 + port + flowinfo + scope_id */
75 0 : }
76 0 : }
77 0 : CHECK_LEFT( 8U ); crds_val->wallclock_nanos = FD_MILLI_TO_NANOSEC( FD_LOAD( ulong, CURSOR ) ); INC( 8U );
78 0 : CHECKED_INC( 2U ); /* shred_version */
79 0 : return BYTES_CONSUMED;
80 0 : }
81 :
82 : static ulong
83 : fd_gossip_msg_crds_vote_parse( fd_gossip_view_crds_value_t * crds_val,
84 : uchar const * payload,
85 : ulong payload_sz,
86 0 : ulong start_offset ) {
87 0 : CHECK_INIT( payload, payload_sz, start_offset );
88 0 : CHECK_LEFT( 1U ); crds_val->vote->index = FD_LOAD( uchar, CURSOR ) ; INC( 1U );
89 0 : CHECK_LEFT( 32U ); crds_val->pubkey_off = CUR_OFFSET ; INC( 32U );
90 0 : ulong transaction_sz;
91 0 : CHECK( fd_txn_parse_core( CURSOR, BYTES_REMAINING, NULL, NULL, &transaction_sz )!=0UL );
92 0 : crds_val->vote->txn_off = CUR_OFFSET;
93 0 : crds_val->vote->txn_sz = transaction_sz;
94 0 : INC( transaction_sz );
95 0 : CHECK_LEFT( 8U ); crds_val->wallclock_nanos = FD_MILLI_TO_NANOSEC( FD_LOAD( ulong, CURSOR ) ); INC( 8U );
96 0 : return BYTES_CONSUMED;
97 0 : }
98 :
99 : static ulong
100 : fd_gossip_msg_crds_lowest_slot_parse( fd_gossip_view_crds_value_t * crds_val,
101 : uchar const * payload,
102 : ulong payload_sz,
103 0 : ulong start_offset ) {
104 0 : CHECK_INIT( payload, payload_sz, start_offset );
105 0 : CHECKED_INC( 1U ); /* deprecated */
106 0 : CHECK_LEFT( 32U ); crds_val->pubkey_off = CUR_OFFSET ; INC( 32U );
107 :
108 0 : CHECKED_INC( 8U ); /* root: deprecated */
109 :
110 0 : CHECK_LEFT( 8U ); crds_val->lowest_slot = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
111 :
112 : /* slots set is deprecated, so we skip it. */
113 0 : CHECK_LEFT( 8U ); ulong slots_len = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
114 0 : CHECKED_INC( slots_len*8U ); /* overflowing this currently doesn't matter, but be careful */
115 :
116 : /* TODO: stash vector<EpochIncompleteSlots> is deprecated, but is hard to skip
117 : since EpochIncompleteSlots is a dynamically sized type. So we fail this
118 : parse if there are any entries. Might be worth implementing a skip instead,
119 : TBD after live testing.
120 : Idea: rip out parser from fd_types
121 : https://github.com/anza-xyz/agave/blob/540d5bc56cd44e3cc61b179bd52e9a782a2c99e4/gossip/src/deprecated.rs#L19 */
122 0 : CHECK_LEFT( 8U ); ulong stash_len = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
123 0 : CHECK( stash_len==0U );
124 :
125 0 : CHECK_LEFT( 8U ); crds_val->wallclock_nanos = FD_MILLI_TO_NANOSEC( FD_LOAD( ulong, CURSOR ) ); INC( 8U );
126 0 : return BYTES_CONSUMED;
127 0 : }
128 :
129 : static ulong
130 : fd_gossip_msg_crds_account_hashes_parse( fd_gossip_view_crds_value_t * crds_val,
131 : uchar const * payload,
132 : ulong payload_sz,
133 0 : ulong start_offset ) {
134 0 : CHECK_INIT( payload, payload_sz, start_offset );
135 0 : CHECK_LEFT( 32U ); crds_val->pubkey_off = CUR_OFFSET ; INC( 32U );
136 0 : CHECK_LEFT( 8U ); ulong hashes_len = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
137 0 : CHECKED_INC( hashes_len*32U ); /* overflowing this currently doesn't matter, but be careful */
138 :
139 0 : CHECK_LEFT( 8U ); crds_val->wallclock_nanos = FD_MILLI_TO_NANOSEC( FD_LOAD( ulong, CURSOR ) ); INC( 8U );
140 0 : return BYTES_CONSUMED;
141 0 : }
142 :
143 : static ulong
144 : fd_gossip_msg_crds_epoch_slots_parse( fd_gossip_view_crds_value_t * crds_val,
145 : uchar const * payload,
146 : ulong payload_sz,
147 0 : ulong start_offset ) {
148 0 : CHECK_INIT( payload, payload_sz, start_offset );
149 0 : CHECK_LEFT( 1U ); crds_val->epoch_slots->index = FD_LOAD( uchar, CURSOR ) ; INC( 1U );
150 0 : CHECK_LEFT( 32U ); crds_val->pubkey_off = CUR_OFFSET ; INC( 32U );
151 0 : CHECK_LEFT( 8U ); ulong slots_len = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
152 :
153 0 : for( ulong i=0UL; i<slots_len; i++ ) {
154 0 : CHECK_LEFT( 4U ); uint is_uncompressed = FD_LOAD( uint, CURSOR ) ; INC( 4U );
155 0 : if( is_uncompressed ) {
156 0 : CHECKED_INC( 8U+8U ); /* first_slot + num */
157 0 : uchar has_bits = 0;
158 0 : CHECK_LEFT( 1U ); has_bits = FD_LOAD( uchar, CURSOR ) ; INC( 1U );
159 0 : if( has_bits ) {
160 0 : CHECK_LEFT( 8U ); ulong bits_len = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
161 0 : CHECKED_INC( bits_len ); /* bitvec<u8> */
162 0 : CHECKED_INC( 8U );
163 0 : }
164 0 : } else {
165 0 : CHECKED_INC( 8U+8U ); /* first_slot + num */
166 0 : CHECK_LEFT( 8U ); ulong compressed_len = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
167 0 : CHECKED_INC( compressed_len ); /* compressed bitvec */
168 0 : }
169 0 : }
170 0 : CHECK_LEFT( 8U ); crds_val->wallclock_nanos = FD_MILLI_TO_NANOSEC( FD_LOAD( ulong, CURSOR ) ); INC( 8U );
171 :
172 0 : return BYTES_CONSUMED;
173 0 : }
174 :
175 : static ulong
176 : fd_gossip_msg_crds_legacy_version_parse( fd_gossip_view_crds_value_t * crds_val,
177 : uchar const * payload,
178 : ulong payload_sz,
179 0 : ulong start_offset ) {
180 0 : CHECK_INIT( payload, payload_sz, start_offset );
181 0 : CHECK_LEFT( 32U ); crds_val->pubkey_off = CUR_OFFSET ; INC( 32U );
182 0 : CHECK_LEFT( 8U ); crds_val->wallclock_nanos = FD_MILLI_TO_NANOSEC( FD_LOAD( ulong, CURSOR ) ); INC( 8U );
183 :
184 0 : CHECKED_INC( 3*2U ); /* major, minor, patch (all u16s)*/
185 0 : CHECK_LEFT( 1U ); uchar has_commit = FD_LOAD( uchar, CURSOR ) ; INC( 1U );
186 0 : if( has_commit ) {
187 0 : CHECKED_INC( 4U );
188 0 : }
189 0 : return BYTES_CONSUMED;
190 0 : }
191 :
192 : static ulong
193 : fd_gossip_msg_crds_version_parse( fd_gossip_view_crds_value_t * crds_val,
194 : uchar const * payload,
195 : ulong payload_sz,
196 0 : ulong start_offset ) {
197 0 : CHECK_INIT( payload, payload_sz, start_offset );
198 0 : INC( fd_gossip_msg_crds_legacy_version_parse( crds_val, payload, payload_sz, start_offset ) );
199 0 : CHECKED_INC( 4U ); /* feature set */
200 0 : return BYTES_CONSUMED;
201 0 : }
202 :
203 : static ulong
204 : fd_gossip_msg_crds_node_instance_parse( fd_gossip_view_crds_value_t * crds_val,
205 : uchar const * payload,
206 : ulong payload_sz,
207 0 : ulong start_offset ) {
208 0 : CHECK_INIT( payload, payload_sz, start_offset );
209 0 : CHECK_LEFT( 32U ); crds_val->pubkey_off = CUR_OFFSET ; INC( 32U );
210 0 : CHECK_LEFT( 8U ); crds_val->wallclock_nanos = FD_MILLI_TO_NANOSEC( FD_LOAD( ulong, CURSOR ) ); INC( 8U );
211 0 : CHECKED_INC( 8U ); /* timestamp (currently unused) */
212 0 : CHECK_LEFT( 8U ); crds_val->node_instance->token = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
213 0 : return BYTES_CONSUMED;
214 0 : }
215 :
216 : static ulong
217 : fd_gossip_msg_crds_duplicate_shred_parse( fd_gossip_view_crds_value_t * crds_val,
218 : uchar const * payload,
219 : ulong payload_sz,
220 0 : ulong start_offset ) {
221 0 : fd_gossip_view_duplicate_shred_t * ds = crds_val->duplicate_shred;
222 :
223 0 : CHECK_INIT( payload, payload_sz, start_offset );
224 :
225 0 : CHECK_LEFT( 2U ); ds->index = FD_LOAD( ushort, CURSOR ) ; INC( 2U );
226 0 : CHECK_LEFT( 32U ); crds_val->pubkey_off = CUR_OFFSET ; INC( 32U );
227 0 : CHECK_LEFT( 8U ); crds_val->wallclock_nanos = FD_MILLI_TO_NANOSEC( FD_LOAD( ulong, CURSOR ) ); INC( 8U );
228 0 : CHECK_LEFT( 8U ); ds->slot = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
229 0 : CHECKED_INC( 4U+1U ); /* (unused) + shred type (unused) */
230 0 : CHECK_LEFT( 1U ); ds->num_chunks = FD_LOAD( uchar, CURSOR ) ; INC( 1U );
231 0 : CHECK_LEFT( 1U ); ds->chunk_index = FD_LOAD( uchar, CURSOR ) ; INC( 1U );
232 0 : CHECK_LEFT( 8U ); ds->chunk_len = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
233 0 : CHECK_LEFT( ds->chunk_len ); ds->chunk_off = CUR_OFFSET ; INC( ds->chunk_len );
234 0 : return BYTES_CONSUMED;
235 0 : }
236 :
237 : static ulong
238 : fd_gossip_msg_crds_snapshot_hashes_parse( fd_gossip_view_crds_value_t * crds_val,
239 : uchar const * payload,
240 : ulong payload_sz,
241 0 : ulong start_offset ) {
242 0 : CHECK_INIT( payload, payload_sz, start_offset );
243 0 : CHECK_LEFT( 32U ); crds_val->pubkey_off = CUR_OFFSET ; INC( 32U );
244 0 : CHECK_LEFT( 40U ); crds_val->snapshot_hashes->full_off = CUR_OFFSET ; INC( 40U );
245 0 : CHECK_LEFT( 8U ); ulong incremental_len = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
246 0 : CHECK_LEFT( incremental_len*40U ); crds_val->snapshot_hashes->inc_off = CUR_OFFSET ; INC( incremental_len*40U );
247 0 : CHECK_LEFT( 8U ); crds_val->wallclock_nanos = FD_MILLI_TO_NANOSEC( FD_LOAD( ulong, CURSOR ) ); INC( 8U );
248 0 : crds_val->snapshot_hashes->inc_len = incremental_len;
249 0 : return BYTES_CONSUMED;
250 0 : }
251 :
252 : static ulong
253 : version_parse( fd_contact_info_t * ci,
254 : uchar const * payload,
255 : ulong payload_sz,
256 0 : ulong start_offset ) {
257 0 : CHECK_INIT( payload, payload_sz, start_offset );
258 0 : ulong decode_sz;
259 0 : READ_CHECKED_COMPACT_U16( decode_sz, ci->version.major, CUR_OFFSET ) ; INC( decode_sz );
260 0 : READ_CHECKED_COMPACT_U16( decode_sz, ci->version.minor, CUR_OFFSET ) ; INC( decode_sz );
261 0 : READ_CHECKED_COMPACT_U16( decode_sz, ci->version.patch, CUR_OFFSET ) ; INC( decode_sz );
262 0 : CHECK_LEFT( 4U ); ci->version.commit = FD_LOAD( uint, CURSOR ) ; INC( 4U );
263 0 : CHECK_LEFT( 4U ); ci->version.feature_set = FD_LOAD( uint, CURSOR ) ; INC( 4U );
264 0 : READ_CHECKED_COMPACT_U16( decode_sz, ci->version.client, CUR_OFFSET ); INC( decode_sz );
265 0 : return BYTES_CONSUMED;
266 0 : }
267 :
268 : /* Contact Infos are checked for the following properties
269 : - All addresses in addrs are unique
270 : - Each socket entry references a unique socket tag
271 : - Socket offsets do not cause an overflow
272 : - All addresses are referenced at least once across all sockets
273 : https://github.com/anza-xyz/agave/blob/540d5bc56cd44e3cc61b179bd52e9a782a2c99e4/gossip/src/contact_info.rs#L599
274 :
275 : We perform additional checks when populating the
276 : contact_info->sockets array:
277 : - Address must be ipv4
278 : - Socket tag must fall within range of tags defined in
279 : fd_gossip_types.c (bounded by FD_CONTACT_INFO_SOCKET_LAST)
280 :
281 : Note that these additional checks are not parser failure conditions.
282 : These sockets are simply skipped when populating
283 : contact_info->sockets (marked as null entries). The CRDS value is
284 : considered valid and is still processed into the CRDS table. */
285 :
286 : #define SET_NAME ip4_seen_set
287 0 : #define SET_MAX (1<<15)
288 : #include "../../util/tmpl/fd_set.c"
289 :
290 : #define SET_NAME ip6_seen_set
291 0 : #define SET_MAX (1<<14)
292 : #include "../../util/tmpl/fd_set.c"
293 :
294 : struct ipv6_addr {
295 : ulong hi;
296 : ulong lo;
297 : };
298 :
299 : typedef struct ipv6_addr ipv6_addr_t;
300 :
301 : static inline ulong
302 0 : ipv6_hash( ipv6_addr_t const * addr ) {
303 0 : return fd_ulong_hash( addr->hi ^ fd_ulong_hash( addr->lo ) );
304 0 : }
305 :
306 : /* Existing sets for socket validation */
307 : #define SET_NAME addr_idx_set
308 : #define SET_MAX FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES
309 : #include "../../util/tmpl/fd_set.c"
310 :
311 : #define SET_NAME socket_tag_set
312 : #define SET_MAX FD_GOSSIP_CONTACT_INFO_MAX_SOCKETS
313 : #include "../../util/tmpl/fd_set.c"
314 :
315 : static ulong
316 : fd_gossip_msg_crds_contact_info_parse( fd_gossip_view_crds_value_t * crds_val,
317 : uchar const * payload,
318 : ulong payload_sz,
319 0 : ulong start_offset ) {
320 0 : CHECK_INIT( payload, payload_sz, start_offset );
321 0 : CHECK_LEFT( 32U ); crds_val->pubkey_off = CUR_OFFSET ; INC( 32U );
322 0 : ulong wallclock = 0UL;
323 0 : INC( decode_u64_varint( payload, payload_sz, CUR_OFFSET, &wallclock ) );
324 0 : crds_val->wallclock_nanos = FD_MILLI_TO_NANOSEC( wallclock );
325 :
326 0 : fd_contact_info_t * ci = crds_val->ci_view->contact_info;
327 0 : fd_memcpy( ci->pubkey.uc, payload + crds_val->pubkey_off, 32UL );
328 0 : ci->wallclock_nanos = crds_val->wallclock_nanos;
329 :
330 0 : CHECK_LEFT( 8U ); ci->instance_creation_wallclock_nanos = FD_MICRO_TO_NANOSEC( FD_LOAD( ulong, CURSOR ) ); INC( 8U );
331 0 : CHECK_LEFT( 2U ); ci->shred_version = FD_LOAD( ushort, CURSOR ) ; INC( 2U );
332 0 : INC( version_parse( ci, payload, payload_sz, CUR_OFFSET ) );
333 :
334 0 : ulong decode_sz, addrs_len;
335 0 : READ_CHECKED_COMPACT_U16( decode_sz, addrs_len, CUR_OFFSET ) ; INC( decode_sz );
336 0 : CHECK( addrs_len<=FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES );
337 :
338 0 : ip4_seen_set_t ip4_seen[ ip4_seen_set_word_cnt ];
339 0 : ip6_seen_set_t ip6_seen[ ip6_seen_set_word_cnt ];
340 0 : ip4_seen_set_new( ip4_seen );
341 0 : ip6_seen_set_new( ip6_seen );
342 :
343 0 : uint ip4_addrs[ FD_GOSSIP_CONTACT_INFO_MAX_ADDRESSES ];
344 :
345 0 : for( ulong i=0UL; i<addrs_len; i++ ) {
346 0 : CHECK_LEFT( 4U ); uchar is_ip6 = FD_LOAD( uchar, CURSOR ) ; INC( 4U );
347 0 : if( FD_LIKELY( !is_ip6 ) ) {
348 0 : CHECK_LEFT( 4U ); ip4_addrs[ i ] = FD_LOAD( uint, CURSOR ) ; INC( 4U );
349 0 : ulong idx = fd_uint_hash( ip4_addrs[ i ] )&(ip4_seen_set_max( ip4_seen )-1);
350 0 : CHECK( !ip4_seen_set_test( ip4_seen, idx ) ); /* Should not be set initially */
351 0 : ip4_seen_set_insert( ip4_seen, idx );
352 0 : } else {
353 : /* TODO: Support IPv6 ... */
354 0 : CHECK_LEFT( 16U ); ipv6_addr_t * ip6_addr = (ipv6_addr_t *)CURSOR ; INC( 16U );
355 0 : ulong idx = ipv6_hash( ip6_addr )&(ip6_seen_set_max( ip6_seen )-1);
356 0 : CHECK( !ip6_seen_set_test( ip6_seen, idx ) );
357 0 : ip6_seen_set_insert( ip6_seen, idx );
358 0 : ip4_addrs[ i ] = 0U; /* Mark as null entry */
359 0 : }
360 0 : }
361 0 : crds_val->ci_view->ip6_cnt = ip6_seen_set_cnt( ip6_seen );
362 :
363 0 : addr_idx_set_t ip_addr_hits[ addr_idx_set_word_cnt ];
364 0 : socket_tag_set_t socket_tag_hits[ socket_tag_set_word_cnt ];
365 0 : addr_idx_set_new( ip_addr_hits );
366 0 : socket_tag_set_new( socket_tag_hits );
367 :
368 0 : ulong sockets_len;
369 0 : READ_CHECKED_COMPACT_U16( decode_sz, sockets_len, CUR_OFFSET ) ; INC( decode_sz );
370 0 : CHECK( sockets_len<=FD_GOSSIP_CONTACT_INFO_MAX_SOCKETS );
371 :
372 0 : fd_memset( ci->sockets, 0, (FD_CONTACT_INFO_SOCKET_LAST+1UL)*sizeof(fd_ip4_port_t) );
373 0 : crds_val->ci_view->unrecognized_socket_tag_cnt = 0UL;
374 :
375 0 : ushort cur_port = 0U;
376 0 : for( ulong i=0UL; i<sockets_len; i++ ) {
377 0 : uchar tag, addr_idx;
378 0 : CHECK_LEFT( 1U ); tag = FD_LOAD( uchar, CURSOR ) ; INC( 1U );
379 0 : CHECK_LEFT( 1U ); addr_idx = FD_LOAD( uchar, CURSOR ) ; INC( 1U );
380 :
381 0 : ushort offset;
382 0 : READ_CHECKED_COMPACT_U16( decode_sz, offset, CUR_OFFSET ) ; INC( decode_sz );
383 0 : CHECK( ((uint)cur_port + (uint)offset)<=(uint)USHORT_MAX ); /* overflow check */
384 0 : cur_port = (ushort)(cur_port + offset);
385 0 : CHECK( !socket_tag_set_test( socket_tag_hits, tag ) ); socket_tag_set_insert( socket_tag_hits, tag );
386 0 : CHECK( addr_idx<addrs_len );
387 0 : addr_idx_set_insert( ip_addr_hits, addr_idx );
388 :
389 0 : if( FD_LIKELY( tag<=FD_CONTACT_INFO_SOCKET_LAST ) ) {
390 0 : if( FD_UNLIKELY( !!ip4_addrs[ addr_idx ] ) ) {
391 0 : ci->sockets[ tag ].addr = ip4_addrs[ addr_idx ];
392 0 : ci->sockets[ tag ].port = fd_ushort_bswap( cur_port ); /* TODO: change this to host order */
393 0 : }
394 0 : } else {
395 0 : crds_val->ci_view->unrecognized_socket_tag_cnt++;
396 0 : }
397 0 : }
398 0 : CHECK( addr_idx_set_cnt( ip_addr_hits )==addrs_len );
399 :
400 : /* extensions are currently unused */
401 0 : READ_CHECKED_COMPACT_U16( decode_sz, crds_val->ci_view->ext_len, CUR_OFFSET ) ; INC( decode_sz );
402 0 : CHECKED_INC( 4*crds_val->ci_view->ext_len );
403 :
404 0 : return BYTES_CONSUMED;
405 0 : }
406 :
407 : static ulong
408 : fd_gossip_msg_crds_last_voted_fork_slots_parse( fd_gossip_view_crds_value_t * crds_val,
409 : uchar const * payload,
410 : ulong payload_sz,
411 0 : ulong start_offset ) {
412 0 : CHECK_INIT( payload, payload_sz, start_offset );
413 0 : CHECK_LEFT( 32U ); crds_val->pubkey_off = CUR_OFFSET ; INC( 32U );
414 0 : CHECK_LEFT( 8U ); crds_val->wallclock_nanos = FD_MILLI_TO_NANOSEC( FD_LOAD( ulong, CURSOR ) ); INC( 8U );
415 0 : CHECK_LEFT( 4U ); uint is_rawoffsets = FD_LOAD( uint, CURSOR ) ; INC( 4U );
416 0 : if( !is_rawoffsets ) {
417 0 : CHECK_LEFT( 8U ); ulong slots_len = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
418 0 : CHECKED_INC( slots_len*4U ); /* RunLengthEncoding */
419 0 : } else {
420 0 : CHECK_LEFT( 1U ); uchar has_bits = FD_LOAD( uchar, CURSOR ) ; INC( 1U );
421 0 : if( has_bits ) {
422 0 : CHECK_LEFT( 8U ); ulong bits_len = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
423 0 : CHECKED_INC( bits_len ); /* bitvec<u8 > */
424 0 : CHECKED_INC( 8U ); /* bits num set */
425 0 : }
426 0 : }
427 0 : CHECKED_INC( 8U+32U+2U ); /* last voted slot + last voted hash + shred version */
428 0 : return BYTES_CONSUMED;
429 0 : }
430 :
431 : static ulong
432 : fd_gossip_msg_crds_restart_heaviest_fork_parse( fd_gossip_view_crds_value_t * crds_val,
433 : uchar const * payload,
434 : ulong payload_sz,
435 0 : ulong start_offset ) {
436 0 : CHECK_INIT( payload, payload_sz, start_offset );
437 0 : CHECK_LEFT( 32U ); crds_val->pubkey_off = CUR_OFFSET ; INC( 32U );
438 0 : CHECK_LEFT( 8U ); crds_val->wallclock_nanos = FD_MILLI_TO_NANOSEC( FD_LOAD( ulong, CURSOR ) ); INC( 8U );
439 0 : CHECKED_INC( 8U+32U+8U+2U ); /* last slot + last slot hash + observed stake + shred version */
440 0 : return BYTES_CONSUMED;
441 0 : }
442 :
443 :
444 : static ulong
445 : fd_gossip_msg_crds_data_parse( fd_gossip_view_crds_value_t * crds_val,
446 : uchar const * payload,
447 : ulong payload_sz,
448 0 : ulong start_offset ) {
449 0 : switch( crds_val->tag ) {
450 0 : case FD_GOSSIP_VALUE_LEGACY_CONTACT_INFO:
451 0 : return fd_gossip_msg_crds_legacy_contact_info_parse( crds_val, payload, payload_sz, start_offset );
452 0 : case FD_GOSSIP_VALUE_VOTE:
453 0 : return fd_gossip_msg_crds_vote_parse( crds_val, payload, payload_sz, start_offset );
454 0 : case FD_GOSSIP_VALUE_LOWEST_SLOT:
455 0 : return fd_gossip_msg_crds_lowest_slot_parse( crds_val, payload, payload_sz, start_offset );
456 0 : case FD_GOSSIP_VALUE_LEGACY_SNAPSHOT_HASHES:
457 0 : case FD_GOSSIP_VALUE_ACCOUNT_HASHES:
458 0 : return fd_gossip_msg_crds_account_hashes_parse( crds_val, payload, payload_sz, start_offset );
459 0 : case FD_GOSSIP_VALUE_EPOCH_SLOTS:
460 0 : return fd_gossip_msg_crds_epoch_slots_parse( crds_val, payload, payload_sz, start_offset );
461 0 : case FD_GOSSIP_VALUE_LEGACY_VERSION:
462 0 : return fd_gossip_msg_crds_legacy_version_parse( crds_val, payload, payload_sz, start_offset );
463 0 : case FD_GOSSIP_VALUE_VERSION:
464 0 : return fd_gossip_msg_crds_version_parse( crds_val, payload, payload_sz, start_offset );
465 0 : case FD_GOSSIP_VALUE_NODE_INSTANCE:
466 0 : return fd_gossip_msg_crds_node_instance_parse( crds_val, payload, payload_sz, start_offset );
467 0 : case FD_GOSSIP_VALUE_DUPLICATE_SHRED:
468 0 : return fd_gossip_msg_crds_duplicate_shred_parse( crds_val, payload, payload_sz, start_offset );
469 0 : case FD_GOSSIP_VALUE_INC_SNAPSHOT_HASHES:
470 0 : return fd_gossip_msg_crds_snapshot_hashes_parse( crds_val, payload, payload_sz, start_offset );
471 0 : case FD_GOSSIP_VALUE_CONTACT_INFO:
472 0 : return fd_gossip_msg_crds_contact_info_parse( crds_val, payload, payload_sz, start_offset );
473 0 : case FD_GOSSIP_VALUE_RESTART_LAST_VOTED_FORK_SLOTS:
474 0 : return fd_gossip_msg_crds_last_voted_fork_slots_parse( crds_val, payload, payload_sz, start_offset );
475 0 : case FD_GOSSIP_VALUE_RESTART_HEAVIEST_FORK:
476 0 : return fd_gossip_msg_crds_restart_heaviest_fork_parse( crds_val, payload, payload_sz, start_offset );
477 0 : default:
478 0 : FD_LOG_WARNING(( "Unknown CRDS value tag %d", crds_val->tag ));
479 0 : return 0;
480 0 : }
481 0 : }
482 :
483 : /* start_offset should point to first byte in first crds value. In
484 : push/pullresponse messages this would be after the crds len */
485 : static ulong
486 : fd_gossip_msg_crds_vals_parse( fd_gossip_view_crds_value_t * crds_values,
487 : ulong crds_values_len,
488 : uchar const * payload,
489 : ulong payload_sz,
490 0 : ulong start_offset ) {
491 0 : CHECK_INIT( payload, payload_sz, start_offset );
492 :
493 0 : for( ulong i=0UL; i<crds_values_len; i++ ) {
494 0 : fd_gossip_view_crds_value_t * crds_view = &crds_values[i];
495 0 : CHECK_LEFT( 64U ); crds_view->signature_off = CUR_OFFSET ; INC( 64U );
496 0 : CHECK_LEFT( 4U ); crds_view->tag = FD_LOAD(uchar, CURSOR ); INC( 4U );
497 0 : ulong crds_data_sz = fd_gossip_msg_crds_data_parse( crds_view, payload, payload_sz, CUR_OFFSET );
498 0 : crds_view->length = (ushort)(crds_data_sz + 64U + 4U); /* signature + tag */
499 0 : INC( crds_data_sz );
500 0 : }
501 0 : return BYTES_CONSUMED;
502 0 : }
503 : static ulong
504 : fd_gossip_msg_ping_pong_parse( fd_gossip_view_t * view,
505 : uchar const * payload,
506 : ulong payload_sz,
507 0 : ulong start_offset ) {
508 0 : CHECK_INIT( payload, payload_sz, start_offset );
509 : /* Ping/Pong share the same memory layout */
510 0 : FD_STATIC_ASSERT( sizeof(fd_gossip_view_ping_t)==sizeof(fd_gossip_view_pong_t), compat );
511 0 : CHECK_LEFT( sizeof(fd_gossip_view_ping_t) );
512 0 : view->ping_pong_off = CUR_OFFSET;
513 0 : INC( sizeof(fd_gossip_view_ping_t) );
514 :
515 0 : return BYTES_CONSUMED;
516 0 : }
517 :
518 : static ulong
519 : fd_gossip_pull_req_parse( fd_gossip_view_t * view,
520 : uchar const * payload,
521 : ulong payload_sz,
522 0 : ulong start_offset ) {
523 0 : CHECK_INIT( payload, payload_sz, start_offset );
524 0 : fd_gossip_view_pull_request_t * pr = view->pull_request;
525 :
526 0 : CHECK_LEFT( 8U ); pr->bloom_keys_len = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
527 0 : CHECK( pr->bloom_keys_len<=((ULONG_MAX-7U)/8U) );
528 0 : CHECK_LEFT( pr->bloom_keys_len*8U ); pr->bloom_keys_offset = CUR_OFFSET ; INC( pr->bloom_keys_len*8U );
529 :
530 0 : uchar has_bits = 0;
531 0 : CHECK_LEFT( 1U ); has_bits = FD_LOAD( uchar, CURSOR ) ; INC( 1U );
532 0 : if( has_bits ) {
533 0 : CHECK_LEFT( 8U ); pr->bloom_len = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
534 0 : CHECK( pr->bloom_len<=((ULONG_MAX-7U)/8U) );
535 0 : CHECK_LEFT( pr->bloom_len*8U ); pr->bloom_bits_offset = CUR_OFFSET ; INC( pr->bloom_len*8U );
536 0 : CHECK_LEFT( 8U ); pr->bloom_bits_cnt = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
537 0 : } else {
538 0 : pr->bloom_len = 0U;
539 0 : }
540 0 : CHECK_LEFT( 8U ); pr->bloom_num_bits_set = FD_LOAD( ulong, CURSOR ); INC( 8U );
541 0 : CHECK_LEFT( 8U ); pr->mask = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
542 0 : CHECK_LEFT( 4U ); pr->mask_bits = FD_LOAD( uint, CURSOR ) ; INC( 4U );
543 :
544 0 : INC( fd_gossip_msg_crds_vals_parse( pr->pr_ci,
545 0 : 1U, /* pull request holds only one contact info */
546 0 : payload,
547 0 : payload_sz,
548 0 : CUR_OFFSET ) );
549 0 : return BYTES_CONSUMED;
550 0 : }
551 :
552 : static ulong
553 : fd_gossip_msg_crds_container_parse( fd_gossip_view_t * view,
554 : uchar const * payload,
555 : ulong payload_sz,
556 0 : ulong start_offset ) {
557 : /* Push and Pull Responses are CRDS composite types, */
558 0 : CHECK_INIT( payload, payload_sz, start_offset );
559 0 : fd_gossip_view_crds_container_t * container = view->tag==FD_GOSSIP_MESSAGE_PUSH ? view->push
560 0 : : view->pull_response;
561 0 : CHECK_LEFT( 32U ); container->from_off = CUR_OFFSET ; INC( 32U );
562 0 : CHECK_LEFT( 8U ); container->crds_values_len = FD_LOAD( ushort, CURSOR ); INC( 8U );
563 0 : CHECK( container->crds_values_len<=FD_GOSSIP_MSG_MAX_CRDS );
564 0 : INC( fd_gossip_msg_crds_vals_parse( container->crds_values,
565 0 : container->crds_values_len,
566 0 : payload,
567 0 : payload_sz,
568 0 : CUR_OFFSET ) );
569 0 : return BYTES_CONSUMED;
570 0 : }
571 :
572 : static ulong
573 : fd_gossip_msg_prune_parse( fd_gossip_view_t * view,
574 : uchar const * payload,
575 : ulong payload_sz,
576 0 : ulong start_offset ) {
577 0 : CHECK_INIT( payload, payload_sz, start_offset );
578 0 : fd_gossip_view_prune_t * prune = view->prune;
579 0 : CHECKED_INC( 32U ); /* pubkey is sent twice */
580 0 : CHECK_LEFT( 32U ); prune->pubkey_off = CUR_OFFSET ; INC( 32U );
581 0 : CHECK_LEFT( 8U ); prune->origins_len = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
582 0 : CHECK( prune->origins_len<=((ULONG_MAX-31U)/32U) );
583 0 : CHECK_LEFT( prune->origins_len*32U ); prune->origins_off = CUR_OFFSET ; INC( prune->origins_len*32U );
584 0 : CHECK_LEFT( 64U ); prune->signature_off = CUR_OFFSET ; INC( 64U );
585 0 : CHECK_LEFT( 32U ); prune->destination_off = CUR_OFFSET ; INC( 32U );
586 0 : CHECK_LEFT( 8U ); prune->wallclock = FD_LOAD( ulong, CURSOR ) ; INC( 8U );
587 :
588 : /* Convert wallclock to nanos */
589 0 : prune->wallclock_nanos = FD_MILLI_TO_NANOSEC( prune->wallclock );
590 :
591 0 : return BYTES_CONSUMED;
592 0 : }
593 :
594 : ulong
595 : fd_gossip_msg_parse( fd_gossip_view_t * view,
596 : uchar const * payload,
597 0 : ulong payload_sz ) {
598 0 : CHECK_INIT( payload, payload_sz, 0U );
599 0 : CHECK( payload_sz<=FD_GOSSIP_MTU );
600 :
601 : /* Extract enum discriminant/tag (4b encoded) */
602 0 : uint tag = 0;
603 0 : CHECK_LEFT( 4U ); tag = FD_LOAD( uchar, CURSOR ); INC( 4U );
604 0 : CHECK( tag<=FD_GOSSIP_MESSAGE_LAST );
605 0 : view->tag = (uchar)tag;
606 :
607 0 : switch( view->tag ){
608 0 : case FD_GOSSIP_MESSAGE_PULL_REQUEST:
609 0 : INC( fd_gossip_pull_req_parse( view, payload, payload_sz, CUR_OFFSET ) );
610 0 : break;
611 0 : case FD_GOSSIP_MESSAGE_PULL_RESPONSE:
612 0 : case FD_GOSSIP_MESSAGE_PUSH:
613 0 : INC( fd_gossip_msg_crds_container_parse( view, payload, payload_sz, CUR_OFFSET ) );
614 0 : break;
615 0 : case FD_GOSSIP_MESSAGE_PRUNE:
616 0 : INC( fd_gossip_msg_prune_parse( view, payload, payload_sz, CUR_OFFSET ) );
617 0 : break;
618 0 : case FD_GOSSIP_MESSAGE_PING:
619 0 : case FD_GOSSIP_MESSAGE_PONG:
620 0 : INC( fd_gossip_msg_ping_pong_parse( view, payload, payload_sz, CUR_OFFSET ) );
621 0 : break;
622 0 : default:
623 0 : FD_LOG_WARNING(( "Unknown Gossip message type %d", view->tag ));
624 0 : return 0;
625 0 : }
626 0 : CHECK( payload_sz==CUR_OFFSET );
627 0 : return BYTES_CONSUMED;
628 0 : }
|