Line data Source code
1 : #include "fd_ping_tracker.h"
2 :
3 : #include "../../ballet/sha256/fd_sha256.h"
4 : #include "../../util/log/fd_log.h"
5 :
6 0 : #define FD_PING_TRACKER_STATE_UNPINGED (0)
7 0 : #define FD_PING_TRACKER_STATE_INVALID (1)
8 0 : #define FD_PING_TRACKER_STATE_VALID (2)
9 0 : #define FD_PING_TRACKER_STATE_VALID_REFRESHING (3)
10 :
11 : struct pubkey_private {
12 : uchar b[ 32UL ];
13 : };
14 :
15 : typedef struct pubkey_private pubkey_private_t;
16 :
17 : struct fd_ping_peer {
18 : fd_ip4_port_t address;
19 : pubkey_private_t identity_pubkey;
20 : uchar ping_token[ 32UL ];
21 : uchar expected_pong_hash[ 32UL ];
22 :
23 : uchar state;
24 :
25 : long next_ping_nanos;
26 : long valid_until_nanos;
27 : long last_rx_nanos;
28 :
29 : ulong pool_next;
30 :
31 : ulong lru_prev;
32 : ulong lru_next;
33 :
34 : ulong map_next;
35 : ulong map_prev;
36 :
37 : union {
38 : struct {
39 : ulong unpinged_next;
40 : ulong unpinged_prev;
41 : };
42 :
43 : struct {
44 : ulong waiting_next;
45 : ulong waiting_prev;
46 : };
47 :
48 : struct {
49 : ulong refreshing_next;
50 : ulong refreshing_prev;
51 : };
52 : };
53 : };
54 :
55 : typedef struct fd_ping_peer fd_ping_peer_t;
56 :
57 : #define POOL_NAME pool
58 0 : #define POOL_NEXT pool_next
59 0 : #define POOL_T fd_ping_peer_t
60 : #include "../../util/tmpl/fd_pool.c"
61 :
62 : #define DLIST_NAME lru_list
63 : #define DLIST_ELE_T fd_ping_peer_t
64 0 : #define DLIST_PREV lru_prev
65 0 : #define DLIST_NEXT lru_next
66 : #include "../../util/tmpl/fd_dlist.c"
67 :
68 : #define DLIST_NAME unpinged_list
69 : #define DLIST_ELE_T fd_ping_peer_t
70 0 : #define DLIST_PREV unpinged_prev
71 0 : #define DLIST_NEXT unpinged_next
72 : #include "../../util/tmpl/fd_dlist.c"
73 :
74 : #define DLIST_NAME waiting_list
75 : #define DLIST_ELE_T fd_ping_peer_t
76 0 : #define DLIST_PREV waiting_prev
77 0 : #define DLIST_NEXT waiting_next
78 : #include "../../util/tmpl/fd_dlist.c"
79 :
80 : #define DLIST_NAME refreshing_list
81 : #define DLIST_ELE_T fd_ping_peer_t
82 0 : #define DLIST_PREV refreshing_prev
83 0 : #define DLIST_NEXT refreshing_next
84 : #include "../../util/tmpl/fd_dlist.c"
85 :
86 : #define MAP_NAME peer_map
87 0 : #define MAP_ELE_T fd_ping_peer_t
88 : #define MAP_KEY_T pubkey_private_t
89 0 : #define MAP_KEY identity_pubkey
90 0 : #define MAP_IDX_T ulong
91 0 : #define MAP_NEXT map_next
92 0 : #define MAP_PREV map_prev
93 0 : #define MAP_KEY_HASH(k,s) ((s) ^ fd_ulong_load_8( (k)->b ))
94 0 : #define MAP_KEY_EQ(k0,k1) (!memcmp((k0)->b, (k1)->b, 32UL))
95 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
96 : #include "../../util/tmpl/fd_map_chain.c"
97 :
98 : struct __attribute__((aligned(FD_PING_TRACKER_ALIGN))) fd_ping_tracker_private {
99 : fd_rng_t * rng;
100 : fd_sha256_t sha[1];
101 :
102 : ulong entrypoints_cnt;
103 : fd_ip4_port_t * entrypoints;
104 :
105 : fd_ping_tracker_metrics_t metrics[1];
106 :
107 : fd_ping_peer_t * pool;
108 : lru_list_t * lru;
109 :
110 : unpinged_list_t * unpinged;
111 : waiting_list_t * waiting;
112 : refreshing_list_t * refreshing;
113 :
114 : peer_map_t * peers;
115 :
116 : fd_ping_tracker_change_fn change_fn;
117 : void * change_fn_ctx;
118 :
119 : ulong magic; /* ==FD_PING_TRACKER_MAGIC */
120 : };
121 :
122 : FD_FN_CONST ulong
123 0 : fd_ping_tracker_align( void ) {
124 0 : return FD_PING_TRACKER_ALIGN;
125 0 : }
126 :
127 : FD_FN_CONST ulong
128 0 : fd_ping_tracker_footprint( ulong entrypoints_len ) {
129 0 : ulong l;
130 0 : l = FD_LAYOUT_INIT;
131 0 : l = FD_LAYOUT_APPEND( l, FD_PING_TRACKER_ALIGN, sizeof(fd_ping_tracker_t) );
132 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_ip4_port_t), entrypoints_len*sizeof(fd_ip4_port_t) );
133 0 : l = FD_LAYOUT_APPEND( l, pool_align(), pool_footprint( FD_PING_TRACKER_MAX ) );
134 0 : l = FD_LAYOUT_APPEND( l, lru_list_align(), lru_list_footprint() );
135 0 : l = FD_LAYOUT_APPEND( l, unpinged_list_align(), unpinged_list_footprint() );
136 0 : l = FD_LAYOUT_APPEND( l, waiting_list_align(), waiting_list_footprint() );
137 0 : l = FD_LAYOUT_APPEND( l, refreshing_list_align(), refreshing_list_footprint() );
138 0 : l = FD_LAYOUT_APPEND( l, peer_map_align(), peer_map_footprint( 8192UL ) );
139 0 : return FD_LAYOUT_FINI( l, FD_PING_TRACKER_ALIGN );
140 0 : }
141 :
142 : void *
143 : fd_ping_tracker_new( void * shmem,
144 : fd_rng_t * rng,
145 : ulong entrypoints_len,
146 : fd_ip4_port_t const * entrypoints,
147 : fd_ping_tracker_change_fn change_fn,
148 0 : void * change_fn_ctx ) {
149 0 : if( FD_UNLIKELY( !shmem ) ) {
150 0 : FD_LOG_WARNING(( "NULL shmem" ));
151 0 : return NULL;
152 0 : }
153 :
154 0 : if( FD_UNLIKELY( !rng ) ) {
155 0 : FD_LOG_WARNING(( "NULL rng" ));
156 0 : return NULL;
157 0 : }
158 :
159 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_ping_tracker_align() ) ) ) {
160 0 : FD_LOG_WARNING(( "misaligned shmem" ));
161 0 : return NULL;
162 0 : }
163 :
164 0 : FD_SCRATCH_ALLOC_INIT( l, shmem );
165 0 : fd_ping_tracker_t * ping_tracker = FD_SCRATCH_ALLOC_APPEND( l, FD_PING_TRACKER_ALIGN, sizeof(fd_ping_tracker_t) );
166 0 : void * _entrypoints = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_ip4_port_t), entrypoints_len*sizeof(fd_ip4_port_t) );
167 0 : void * _pool = FD_SCRATCH_ALLOC_APPEND( l, pool_align(), pool_footprint( FD_PING_TRACKER_MAX ) );
168 0 : void * _lru = FD_SCRATCH_ALLOC_APPEND( l, lru_list_align(), lru_list_footprint() );
169 0 : void * _unpinged = FD_SCRATCH_ALLOC_APPEND( l, unpinged_list_align(), unpinged_list_footprint() );
170 0 : void * _waiting = FD_SCRATCH_ALLOC_APPEND( l, waiting_list_align(), waiting_list_footprint() );
171 0 : void * _refreshing = FD_SCRATCH_ALLOC_APPEND( l, refreshing_list_align(), refreshing_list_footprint() );
172 0 : void * _peers = FD_SCRATCH_ALLOC_APPEND( l, peer_map_align(), peer_map_footprint( 8192UL ) );
173 :
174 0 : ping_tracker->rng = rng;
175 0 : ping_tracker->pool = pool_join( pool_new( _pool, FD_PING_TRACKER_MAX ) );
176 0 : FD_TEST( ping_tracker->pool );
177 0 : ping_tracker->lru = lru_list_join( lru_list_new( _lru ) );
178 0 : FD_TEST( ping_tracker->lru );
179 0 : ping_tracker->unpinged = unpinged_list_join( unpinged_list_new( _unpinged ) );
180 0 : FD_TEST( ping_tracker->unpinged );
181 0 : ping_tracker->waiting = waiting_list_join( waiting_list_new( _waiting ) );
182 0 : FD_TEST( ping_tracker->waiting );
183 0 : ping_tracker->refreshing = refreshing_list_join( refreshing_list_new( _refreshing ) );
184 0 : FD_TEST( ping_tracker->refreshing );
185 0 : ping_tracker->peers = peer_map_join( peer_map_new( _peers, 8192UL, fd_rng_ulong( rng ) ) );
186 0 : FD_TEST( ping_tracker->peers );
187 :
188 0 : ping_tracker->entrypoints_cnt = entrypoints_len;
189 0 : ping_tracker->entrypoints = (fd_ip4_port_t *)_entrypoints;
190 0 : fd_memcpy( ping_tracker->entrypoints, entrypoints, entrypoints_len*sizeof(fd_ip4_port_t) );
191 :
192 0 : ping_tracker->change_fn = change_fn;
193 0 : ping_tracker->change_fn_ctx = change_fn_ctx;
194 :
195 0 : FD_TEST( fd_sha256_join( fd_sha256_new( ping_tracker->sha ) ) );
196 :
197 0 : fd_memset( ping_tracker->metrics, 0, sizeof(fd_ping_tracker_metrics_t) );
198 :
199 0 : FD_COMPILER_MFENCE();
200 0 : FD_VOLATILE( ping_tracker->magic ) = FD_PING_TRACKER_MAGIC;
201 0 : FD_COMPILER_MFENCE();
202 :
203 0 : return (void *)ping_tracker;
204 0 : }
205 :
206 : fd_ping_tracker_t *
207 0 : fd_ping_tracker_join( void * shpt ) {
208 0 : if( FD_UNLIKELY( !shpt ) ) {
209 0 : FD_LOG_WARNING(( "NULL shpt" ));
210 0 : return NULL;
211 0 : }
212 :
213 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shpt, fd_ping_tracker_align() ) ) ) {
214 0 : FD_LOG_WARNING(( "misaligned shpt" ));
215 0 : return NULL;
216 0 : }
217 :
218 0 : fd_ping_tracker_t * ping_tracker = (fd_ping_tracker_t *)shpt;
219 :
220 0 : if( FD_UNLIKELY( ping_tracker->magic!=FD_PING_TRACKER_MAGIC ) ) {
221 0 : FD_LOG_WARNING(( "bad magic" ));
222 0 : return NULL;
223 0 : }
224 :
225 0 : return ping_tracker;
226 0 : }
227 :
228 : static inline void
229 : hash_ping_token( uchar const * ping_token,
230 : uchar expected_pong_token[ static 32UL ],
231 0 : fd_sha256_t * sha ) {
232 0 : fd_sha256_init( sha );
233 0 : fd_sha256_append( sha, "SOLANA_PING_PONG", 16UL );
234 0 : fd_sha256_append( sha, ping_token, 32UL );
235 0 : fd_sha256_fini( sha, expected_pong_token );
236 0 : }
237 :
238 : static void
239 : remove_tracking( fd_ping_tracker_t * ping_tracker,
240 0 : fd_ping_peer_t * peer ) {
241 0 : if( FD_UNLIKELY( peer->state==FD_PING_TRACKER_STATE_UNPINGED ) ) unpinged_list_ele_remove( ping_tracker->unpinged, peer, ping_tracker->pool );
242 0 : else if( FD_LIKELY( peer->state==FD_PING_TRACKER_STATE_VALID ) ) waiting_list_ele_remove( ping_tracker->waiting, peer, ping_tracker->pool );
243 0 : else refreshing_list_ele_remove( ping_tracker->refreshing, peer, ping_tracker->pool );
244 0 : }
245 :
246 : static void
247 : generate_ping_token( fd_ping_peer_t * peer,
248 0 : fd_rng_t * rng ) {
249 0 : fd_memcpy( peer->ping_token, "SOLANA_PING_PONG", 16UL );
250 0 : for( ulong i=16UL; i<32UL; i++ ) peer->ping_token[ i ] = fd_rng_uchar( rng );
251 0 : }
252 :
253 : static inline int
254 : is_entrypoint( fd_ping_tracker_t const * ping_tracker,
255 0 : fd_ip4_port_t peer_addr ) {
256 0 : for( ulong i=0UL; i<ping_tracker->entrypoints_cnt; i++ ) {
257 0 : if( FD_UNLIKELY( peer_addr.addr==ping_tracker->entrypoints[ i ].addr && peer_addr.port==ping_tracker->entrypoints[ i ].port ) ) return 1;
258 0 : }
259 0 : return 0;
260 0 : }
261 :
262 : void
263 : fd_ping_tracker_track( fd_ping_tracker_t * ping_tracker,
264 : uchar const * peer_pubkey,
265 : ulong peer_stake,
266 : fd_ip4_port_t peer_address,
267 0 : long now ) {
268 0 : fd_ping_peer_t * peer = peer_map_ele_query( ping_tracker->peers, fd_type_pun_const( peer_pubkey ), NULL, ping_tracker->pool );
269 :
270 0 : if( FD_UNLIKELY( !peer ) ) {
271 0 : if( FD_LIKELY( peer_stake>=1000000000UL ) ) return;
272 0 : if( FD_UNLIKELY( is_entrypoint( ping_tracker, peer_address ) ) ) return;
273 :
274 0 : if( FD_UNLIKELY( !pool_free( ping_tracker->pool ) ) ) {
275 0 : peer = lru_list_ele_pop_head( ping_tracker->lru, ping_tracker->pool );
276 0 : remove_tracking( ping_tracker, peer );
277 0 : peer_map_ele_remove_fast( ping_tracker->peers, peer, ping_tracker->pool );
278 0 : if( FD_LIKELY( peer->state==FD_PING_TRACKER_STATE_VALID || peer->state==FD_PING_TRACKER_STATE_VALID_REFRESHING ) ) {
279 0 : ping_tracker->change_fn( ping_tracker->change_fn_ctx, peer->identity_pubkey.b, peer->address, now, FD_PING_TRACKER_CHANGE_TYPE_INACTIVE );
280 0 : }
281 0 : switch( peer->state ) {
282 0 : case FD_PING_TRACKER_STATE_UNPINGED: ping_tracker->metrics->unpinged_cnt--; break;
283 0 : case FD_PING_TRACKER_STATE_INVALID: ping_tracker->metrics->invalid_cnt--; break;
284 0 : case FD_PING_TRACKER_STATE_VALID: ping_tracker->metrics->valid_cnt--; break;
285 0 : case FD_PING_TRACKER_STATE_VALID_REFRESHING: ping_tracker->metrics->refreshing_cnt--; break;
286 0 : default: FD_LOG_ERR(( "Unknown state %d", peer->state )); return;
287 0 : }
288 0 : ping_tracker->metrics->peers_evicted++;
289 0 : } else {
290 0 : peer = pool_ele_acquire( ping_tracker->pool );
291 0 : }
292 :
293 0 : fd_memcpy( peer->identity_pubkey.b, peer_pubkey, 32UL );
294 0 : peer->address = peer_address;
295 0 : peer->valid_until_nanos = 0L;
296 0 : peer->next_ping_nanos = now;
297 0 : peer->state = FD_PING_TRACKER_STATE_UNPINGED;
298 0 : ping_tracker->metrics->unpinged_cnt++;
299 0 : ping_tracker->metrics->tracked_cnt++;
300 :
301 0 : generate_ping_token( peer, ping_tracker->rng );
302 0 : hash_ping_token( peer->ping_token, peer->expected_pong_hash, ping_tracker->sha );
303 :
304 0 : unpinged_list_ele_push_head( ping_tracker->unpinged, peer, ping_tracker->pool );
305 0 : peer_map_ele_insert( ping_tracker->peers, peer, ping_tracker->pool );
306 0 : lru_list_ele_push_tail( ping_tracker->lru, peer, ping_tracker->pool );
307 0 : } else {
308 0 : if( FD_LIKELY( peer_stake>=1000000000UL || is_entrypoint( ping_tracker, peer_address ) ) ) {
309 : /* Node went from unstaked (or low staked) to >=1 SOL, or to being
310 : an entrypoint. No longer need to ping it. */
311 0 : peer_map_ele_remove_fast( ping_tracker->peers, peer, ping_tracker->pool );
312 0 : lru_list_ele_remove( ping_tracker->lru, peer, ping_tracker->pool );
313 0 : remove_tracking( ping_tracker, peer );
314 0 : pool_ele_release( ping_tracker->pool, peer );
315 0 : if( FD_LIKELY( peer->state==FD_PING_TRACKER_STATE_VALID || peer->state==FD_PING_TRACKER_STATE_VALID_REFRESHING ) ) {
316 0 : ping_tracker->change_fn( ping_tracker->change_fn_ctx, peer->identity_pubkey.b, peer->address, now, FD_PING_TRACKER_CHANGE_TYPE_INACTIVE_STAKED );
317 0 : }
318 0 : ping_tracker->metrics->stake_changed_cnt++;
319 0 : switch( peer->state ) {
320 0 : case FD_PING_TRACKER_STATE_UNPINGED: ping_tracker->metrics->unpinged_cnt--; break;
321 0 : case FD_PING_TRACKER_STATE_INVALID: ping_tracker->metrics->invalid_cnt--; break;
322 0 : case FD_PING_TRACKER_STATE_VALID: ping_tracker->metrics->valid_cnt--; break;
323 0 : case FD_PING_TRACKER_STATE_VALID_REFRESHING: ping_tracker->metrics->refreshing_cnt--; break;
324 0 : default: FD_LOG_ERR(( "Unknown state %d", peer->state )); return;
325 0 : }
326 0 : return;
327 0 : }
328 :
329 0 : if( FD_UNLIKELY( peer_address.addr!=peer->address.addr || peer_address.port!=peer->address.port ) ) {
330 : /* Node changed address, update the address. Any existing pongs
331 : are no longer valid. */
332 0 : peer->address = peer_address;
333 0 : peer->valid_until_nanos = 0UL;
334 0 : remove_tracking( ping_tracker, peer );
335 0 : if( FD_LIKELY( peer->state==FD_PING_TRACKER_STATE_VALID || peer->state==FD_PING_TRACKER_STATE_VALID_REFRESHING ) ) {
336 0 : ping_tracker->change_fn( ping_tracker->change_fn_ctx, peer->identity_pubkey.b, peer->address, now, FD_PING_TRACKER_CHANGE_TYPE_INACTIVE );
337 0 : }
338 0 : ping_tracker->metrics->address_changed_cnt++;
339 0 : switch( peer->state ) {
340 0 : case FD_PING_TRACKER_STATE_UNPINGED: ping_tracker->metrics->unpinged_cnt--; break;
341 0 : case FD_PING_TRACKER_STATE_INVALID: ping_tracker->metrics->invalid_cnt--; break;
342 0 : case FD_PING_TRACKER_STATE_VALID: ping_tracker->metrics->valid_cnt--; break;
343 0 : case FD_PING_TRACKER_STATE_VALID_REFRESHING: ping_tracker->metrics->refreshing_cnt--; break;
344 0 : default: FD_LOG_ERR(( "Unknown state %d", peer->state )); return;
345 0 : }
346 0 : peer->next_ping_nanos = now;
347 0 : peer->state = FD_PING_TRACKER_STATE_UNPINGED;
348 0 : ping_tracker->metrics->unpinged_cnt++;
349 0 : generate_ping_token( peer, ping_tracker->rng );
350 0 : hash_ping_token( peer->ping_token, peer->expected_pong_hash, ping_tracker->sha );
351 :
352 0 : unpinged_list_ele_push_head( ping_tracker->unpinged, peer, ping_tracker->pool );
353 0 : }
354 0 : }
355 :
356 0 : peer->last_rx_nanos = now;
357 0 : lru_list_ele_remove( ping_tracker->lru, peer, ping_tracker->pool );
358 0 : lru_list_ele_push_tail( ping_tracker->lru, peer, ping_tracker->pool );
359 0 : }
360 :
361 : void
362 : fd_ping_tracker_register( fd_ping_tracker_t * ping_tracker,
363 : uchar const * peer_pubkey,
364 : ulong peer_stake,
365 : fd_ip4_port_t peer_address,
366 : uchar const * pong_token,
367 0 : long now ) {
368 0 : if( FD_UNLIKELY( peer_stake>=1000000000UL ) ) {
369 0 : ping_tracker->metrics->pong_result[ 0UL ]++;
370 0 : return;
371 0 : }
372 0 : if( FD_UNLIKELY( is_entrypoint( ping_tracker, peer_address ) ) ) {
373 0 : ping_tracker->metrics->pong_result[ 1UL ]++;
374 0 : return;
375 0 : }
376 :
377 0 : fd_ping_peer_t * peer = peer_map_ele_query( ping_tracker->peers, fd_type_pun_const( peer_pubkey ), NULL, ping_tracker->pool );
378 0 : if( FD_UNLIKELY( !peer ) ) {
379 0 : ping_tracker->metrics->pong_result[ 2UL ]++;
380 0 : return;
381 0 : }
382 :
383 0 : if( FD_UNLIKELY( peer_address.addr!=peer->address.addr || peer_address.port!=peer->address.port ) ) {
384 0 : ping_tracker->metrics->pong_result[ 3UL ]++;
385 0 : return;
386 0 : }
387 0 : if( FD_UNLIKELY( memcmp( pong_token, peer->expected_pong_hash, 32UL ) ) ) {
388 0 : ping_tracker->metrics->pong_result[ 4UL ]++;
389 0 : return;
390 0 : }
391 :
392 0 : remove_tracking( ping_tracker, peer );
393 0 : peer->valid_until_nanos = now+20L*60L*1000L*1000L*1000L; /* 20 mintues of validity */
394 0 : peer->next_ping_nanos = now+18L*60L*1000L*1000L*1000L; /* 18 minutes til we start trying to refresh */
395 0 : if( FD_UNLIKELY( peer->state==FD_PING_TRACKER_STATE_INVALID || peer->state==FD_PING_TRACKER_STATE_UNPINGED ) ) {
396 0 : ping_tracker->change_fn( ping_tracker->change_fn_ctx, peer->identity_pubkey.b, peer->address, now, FD_PING_TRACKER_CHANGE_TYPE_ACTIVE );
397 0 : }
398 0 : switch( peer->state ) {
399 0 : case FD_PING_TRACKER_STATE_UNPINGED: ping_tracker->metrics->unpinged_cnt--; break;
400 0 : case FD_PING_TRACKER_STATE_INVALID: ping_tracker->metrics->invalid_cnt--; break;
401 0 : case FD_PING_TRACKER_STATE_VALID: ping_tracker->metrics->valid_cnt--; break;
402 0 : case FD_PING_TRACKER_STATE_VALID_REFRESHING: ping_tracker->metrics->refreshing_cnt--; break;
403 0 : default: FD_LOG_ERR(( "Unknown state %d", peer->state )); return;
404 0 : }
405 0 : peer->state = FD_PING_TRACKER_STATE_VALID;
406 0 : ping_tracker->metrics->valid_cnt++;
407 0 : waiting_list_ele_push_tail( ping_tracker->waiting, peer, ping_tracker->pool );
408 0 : ping_tracker->metrics->pong_result[ 5UL ]++;
409 0 : }
410 :
411 : int
412 : fd_ping_tracker_active( fd_ping_tracker_t * ping_tracker,
413 0 : uchar const * peer_pubkey ) {
414 0 : fd_ping_peer_t * peer = peer_map_ele_query( ping_tracker->peers, fd_type_pun_const( peer_pubkey ), NULL, ping_tracker->pool );
415 0 : if( FD_UNLIKELY( !peer ) ) return 0;
416 0 : return peer->state==FD_PING_TRACKER_STATE_VALID || peer->state==FD_PING_TRACKER_STATE_VALID_REFRESHING;
417 0 : }
418 :
419 : int
420 : fd_ping_tracker_pop_request( fd_ping_tracker_t * ping_tracker,
421 : long now,
422 : uchar const ** out_peer_pubkey,
423 : fd_ip4_port_t const ** out_peer_address,
424 0 : uchar const ** out_token ) {
425 0 : if( FD_UNLIKELY( !unpinged_list_is_empty( ping_tracker->unpinged, ping_tracker->pool ) ) ) {
426 0 : fd_ping_peer_t * unpinged = unpinged_list_ele_pop_head( ping_tracker->unpinged, ping_tracker->pool );
427 0 : FD_TEST( unpinged->state==FD_PING_TRACKER_STATE_UNPINGED );
428 0 : refreshing_list_ele_push_tail( ping_tracker->refreshing, unpinged, ping_tracker->pool );
429 0 : unpinged->state = FD_PING_TRACKER_STATE_INVALID;
430 0 : ping_tracker->metrics->unpinged_cnt--;
431 0 : ping_tracker->metrics->invalid_cnt++;
432 0 : unpinged->next_ping_nanos = now+1L*1000L*1000L*1000L;
433 0 : *out_peer_pubkey = unpinged->identity_pubkey.b;
434 0 : *out_peer_address = &unpinged->address;
435 0 : *out_token = unpinged->ping_token;
436 0 : return 1;
437 0 : }
438 :
439 0 : for(;;) {
440 0 : fd_ping_peer_t * peer_refreshing = NULL;
441 0 : if( FD_UNLIKELY( !refreshing_list_is_empty( ping_tracker->refreshing, ping_tracker->pool ) ) ) peer_refreshing = refreshing_list_ele_peek_head( ping_tracker->refreshing, ping_tracker->pool );
442 0 : fd_ping_peer_t * peer_waiting = NULL;
443 0 : if( FD_UNLIKELY( !waiting_list_is_empty( ping_tracker->waiting, ping_tracker->pool ) ) ) peer_waiting = waiting_list_ele_peek_head( ping_tracker->waiting, ping_tracker->pool );
444 :
445 0 : fd_ping_peer_t * next;
446 0 : if( FD_UNLIKELY( !peer_refreshing && !peer_waiting ) ) return 0;
447 0 : else if( FD_UNLIKELY( peer_refreshing && !peer_waiting ) ) next = peer_refreshing;
448 0 : else if( FD_UNLIKELY( !peer_refreshing && peer_waiting ) ) next = peer_waiting;
449 0 : else if( FD_UNLIKELY( peer_waiting->next_ping_nanos<peer_refreshing->next_ping_nanos ) ) next = peer_waiting;
450 0 : else next = peer_refreshing;
451 :
452 0 : FD_TEST( next->state!=FD_PING_TRACKER_STATE_UNPINGED );
453 0 : FD_TEST( next->next_ping_nanos );
454 0 : if( FD_LIKELY( next->state!=FD_PING_TRACKER_STATE_INVALID ) ) FD_TEST( next->valid_until_nanos );
455 0 : else FD_TEST( !next->valid_until_nanos );
456 :
457 0 : if( FD_UNLIKELY( next->last_rx_nanos<now-20L*1000L*1000L*1000L ) ) {
458 : /* The peer is no longer sending us contact information, no need
459 : to ping it and instead remove it from the table. */
460 0 : peer_map_ele_remove_fast( ping_tracker->peers, next, ping_tracker->pool );
461 0 : lru_list_ele_remove( ping_tracker->lru, next, ping_tracker->pool );
462 0 : remove_tracking( ping_tracker, next );
463 0 : pool_ele_release( ping_tracker->pool, next );
464 0 : if( FD_LIKELY( next->state==FD_PING_TRACKER_STATE_VALID || next->state==FD_PING_TRACKER_STATE_VALID_REFRESHING ) ) {
465 0 : ping_tracker->change_fn( ping_tracker->change_fn_ctx, next->identity_pubkey.b, next->address, now, FD_PING_TRACKER_CHANGE_TYPE_INACTIVE );
466 0 : }
467 0 : switch( next->state ) {
468 0 : case FD_PING_TRACKER_STATE_UNPINGED: ping_tracker->metrics->unpinged_cnt--; break;
469 0 : case FD_PING_TRACKER_STATE_INVALID: ping_tracker->metrics->invalid_cnt--; break;
470 0 : case FD_PING_TRACKER_STATE_VALID: ping_tracker->metrics->valid_cnt--; break;
471 0 : case FD_PING_TRACKER_STATE_VALID_REFRESHING: ping_tracker->metrics->refreshing_cnt--; break;
472 0 : default: FD_LOG_ERR(( "Unknown state %d", next->state ));
473 0 : }
474 0 : continue;
475 0 : }
476 :
477 : /* The next ping we want to send is still in the future, so do
478 : nothing for now. */
479 0 : if( FD_LIKELY( next->next_ping_nanos>now ) ) return 0;
480 :
481 0 : if( FD_LIKELY( next==peer_refreshing ) ) refreshing_list_ele_pop_head( ping_tracker->refreshing, ping_tracker->pool );
482 0 : else if( FD_LIKELY( next==peer_waiting ) ) waiting_list_ele_pop_head( ping_tracker->waiting, ping_tracker->pool );
483 0 : else FD_LOG_CRIT(( "impossible" ));
484 :
485 : /* Push the element to the back of the refreshing list now, so it
486 : starts getting pinged every second. */
487 0 : refreshing_list_ele_push_tail( ping_tracker->refreshing, next, ping_tracker->pool );
488 0 : if( FD_LIKELY( next->state==FD_PING_TRACKER_STATE_VALID ) ) {
489 0 : next->state = FD_PING_TRACKER_STATE_VALID_REFRESHING;
490 0 : ping_tracker->metrics->valid_cnt--;
491 0 : ping_tracker->metrics->refreshing_cnt++;
492 0 : } else if( FD_LIKELY( next->state==FD_PING_TRACKER_STATE_VALID_REFRESHING && next->valid_until_nanos<=now ) ) {
493 0 : ping_tracker->change_fn( ping_tracker->change_fn_ctx, next->identity_pubkey.b, next->address, now, FD_PING_TRACKER_CHANGE_TYPE_INACTIVE );
494 0 : switch( next->state ) {
495 0 : case FD_PING_TRACKER_STATE_UNPINGED: ping_tracker->metrics->unpinged_cnt--; break;
496 0 : case FD_PING_TRACKER_STATE_INVALID: ping_tracker->metrics->invalid_cnt--; break;
497 0 : case FD_PING_TRACKER_STATE_VALID: ping_tracker->metrics->valid_cnt--; break;
498 0 : case FD_PING_TRACKER_STATE_VALID_REFRESHING: ping_tracker->metrics->refreshing_cnt--; break;
499 0 : default: FD_LOG_ERR(( "Unknown state %d", next->state ));
500 0 : }
501 0 : next->state = FD_PING_TRACKER_STATE_INVALID;
502 0 : next->valid_until_nanos = 0L;
503 0 : ping_tracker->metrics->invalid_cnt++;
504 0 : }
505 0 : next->next_ping_nanos = now+1L*1000L*1000L*1000L;
506 0 : *out_peer_pubkey = next->identity_pubkey.b;
507 0 : *out_peer_address = &next->address;
508 0 : *out_token = next->ping_token;
509 0 : return 1;
510 0 : }
511 0 : }
512 :
513 : fd_ping_tracker_metrics_t const *
514 0 : fd_ping_tracker_metrics( fd_ping_tracker_t const * ping_tracker ) {
515 0 : return ping_tracker->metrics;
516 0 : }
|