Line data Source code
1 : /* fd_rserve_tile serves incoming repair requests from other nodes */
2 :
3 : #define _GNU_SOURCE
4 : #include <errno.h>
5 : #include <fcntl.h>
6 :
7 : #include "fd_rserve.h"
8 : #include "fd_repair.h"
9 : #include "../../disco/fd_disco_base.h"
10 : #include "../../disco/keyguard/fd_keyguard_client.h"
11 : #include "../../disco/keyguard/fd_keyguard.h"
12 : #include "../../disco/keyguard/fd_keyload.h"
13 : #include "../../disco/keyguard/fd_keyswitch.h"
14 : #include "../../disco/metrics/fd_metrics.h"
15 : #include "../../disco/net/fd_net_tile.h"
16 : #include "../../disco/shred/fd_shred_tile.h"
17 : #include "../../disco/store/fd_store.h"
18 : #include "../../disco/topo/fd_topo.h"
19 : #include "../../util/pod/fd_pod_format.h"
20 : #include "../../flamenco/gossip/fd_gossip_message.h"
21 : #include "../../util/net/fd_net_headers.h"
22 :
23 : #include "generated/fd_rserve_tile_seccomp.h"
24 :
25 0 : #define IN_KIND_NET (0)
26 0 : #define IN_KIND_SIGN (1)
27 0 : #define IN_KIND_SHRED (2)
28 :
29 : #define MAX_IN_LINKS FD_TOPO_MAX_TILE_IN_LINKS
30 :
31 0 : #define FD_RSERVE_MAX_PACKET_SIZE 1232
32 :
33 : /* The maximum number of parent slots to look for. */
34 0 : #define FD_RSERVE_MAX_ORPHAN_SLOTS 11
35 :
36 : /* 10 minutes in milliseconds. */
37 : #define FD_RSERVE_SIGNED_REPAIR_WINDOW (60L*10L*1000L)
38 :
39 : /* static map from request type to response metric array index */
40 : static uint response_metric_index[FD_REPAIR_KIND_ORPHAN + 1] = {
41 : [FD_REPAIR_KIND_PING] = FD_METRICS_ENUM_RSERVE_SENT_RESPONSE_TYPES_V_PING_IDX,
42 : [FD_REPAIR_KIND_SHRED] = FD_METRICS_ENUM_RSERVE_SENT_RESPONSE_TYPES_V_WINDOW_IDX,
43 : [FD_REPAIR_KIND_HIGHEST_SHRED] = FD_METRICS_ENUM_RSERVE_SENT_RESPONSE_TYPES_V_HIGHEST_WINDOW_IDX,
44 : [FD_REPAIR_KIND_ORPHAN] = FD_METRICS_ENUM_RSERVE_SENT_RESPONSE_TYPES_V_ORPHAN_IDX,
45 : };
46 :
47 : /* static map from request type to received request metric array index */
48 : static uint request_metric_index[FD_REPAIR_KIND_ORPHAN + 1] = {
49 : [FD_REPAIR_KIND_PONG] = FD_METRICS_ENUM_RSERVE_REQUEST_TYPES_V_PONG_IDX,
50 : [FD_REPAIR_KIND_SHRED] = FD_METRICS_ENUM_RSERVE_REQUEST_TYPES_V_WINDOW_INDEX_IDX,
51 : [FD_REPAIR_KIND_HIGHEST_SHRED] = FD_METRICS_ENUM_RSERVE_REQUEST_TYPES_V_HIGHEST_WINDOW_INDEX_IDX,
52 : [FD_REPAIR_KIND_ORPHAN] = FD_METRICS_ENUM_RSERVE_REQUEST_TYPES_V_ORPHAN_IDX,
53 : };
54 :
55 :
56 : typedef union {
57 : struct {
58 : fd_wksp_t * mem;
59 : ulong chunk0;
60 : ulong wmark;
61 : ulong mtu;
62 : };
63 : fd_net_rx_bounds_t net_rx;
64 : } in_ctx_t;
65 :
66 : typedef struct ctx {
67 : fd_net_rx_bounds_t net_rx;
68 :
69 : uint in_kind [ MAX_IN_LINKS ];
70 : in_ctx_t in_links[ MAX_IN_LINKS ];
71 :
72 : uint net_out_idx;
73 : fd_wksp_t * net_out_mem;
74 : ulong net_out_chunk0;
75 : ulong net_out_wmark;
76 : ulong net_out_chunk;
77 :
78 : ulong seed;
79 : uchar rserve_secret[ 32 ];
80 : fd_rserve_t * rserve;
81 : fd_store_t * store;
82 : int disk_fd;
83 : ulong max_shreds_per_block;
84 :
85 : /* Used for verifying incoming requests, and signing outgoing responses. */
86 : fd_sha512_t sha512[1];
87 : fd_keyguard_client_t keyguard_client[1];
88 : fd_keyswitch_t * keyswitch;
89 : fd_pubkey_t identity_public_key;
90 : int halt_signing;
91 :
92 : fd_ip4_udp_hdrs_t serve_hdr[1];
93 : ushort net_id;
94 :
95 : struct {
96 : ulong received_request_count[FD_METRICS_ENUM_RSERVE_REQUEST_TYPES_CNT];
97 : ulong received_request_bytes;
98 : ulong received_malformed_count[FD_METRICS_ENUM_RSERVE_MALFORMED_TYPES_CNT];
99 :
100 : ulong send_pkt_cnt;
101 : ulong sent_pkt_types [FD_METRICS_ENUM_RSERVE_SENT_RESPONSE_TYPES_CNT];
102 : ulong sent_response_bytes;
103 :
104 : ulong missed_pkt_types[FD_METRICS_ENUM_RSERVE_SENT_RESPONSE_TYPES_CNT];
105 : ulong fail_sigverify_request;
106 : ulong fail_own_key;
107 : ulong fail_not_for_us;
108 : ulong fail_invalid_token;
109 : ulong fail_outdated;
110 : ulong fail_invalid_shred_idx;
111 : ulong fail_ping_cache_lookup;
112 : ulong disk_read_busy;
113 : ulong disk_read_miss;
114 : ulong disk_read_scan_limit;
115 : ulong disk_read_success;
116 : ulong disk_read_bytes;
117 : ulong shreds_current;
118 : ulong disk_current_bytes;
119 : ulong disk_allocated_bytes;
120 : fd_histf_t disk_write_timing[1];
121 : ulong disk_inserted;
122 : ulong disk_write_failed;
123 : ulong disk_write_bytes;
124 : fd_histf_t fec_preevict_timing[1];
125 : ulong fec_preevict_write_cnt;
126 : ulong fec_preevict_write_bytes;
127 :
128 : ulong ping_cache_entries;
129 : ulong ping_cache_evictions;
130 : } metrics[ 1 ];
131 : } ctx_t;
132 :
133 : FD_FN_CONST static inline ulong
134 0 : scratch_align( void ) {
135 0 : return 128UL;
136 0 : }
137 :
138 : FD_FN_PURE static inline ulong
139 0 : scratch_footprint( fd_topo_tile_t const * tile FD_PARAM_UNUSED ) {
140 0 : ulong l = FD_LAYOUT_INIT;
141 0 : l = FD_LAYOUT_APPEND( l, alignof(ctx_t), sizeof(ctx_t) );
142 0 : l = FD_LAYOUT_APPEND( l, fd_rserve_align(), fd_rserve_footprint( tile->rserve.ping_cache_entries) );
143 0 : return FD_LAYOUT_FINI( l, scratch_align() );
144 0 : }
145 :
146 : static void
147 : send_packet( ctx_t * ctx,
148 : fd_stem_context_t * stem,
149 : uint dst_ip_addr,
150 : ushort dst_port,
151 : uint src_ip_addr,
152 : uchar const * payload,
153 : ulong payload_sz,
154 0 : ulong tsorig ) {
155 0 : ctx->metrics->send_pkt_cnt++;
156 0 : ctx->metrics->sent_response_bytes += payload_sz;
157 0 : uchar * packet = fd_chunk_to_laddr( ctx->net_out_mem, ctx->net_out_chunk );
158 0 : fd_ip4_udp_hdrs_t * hdr = (fd_ip4_udp_hdrs_t *)packet;
159 0 : *hdr = *ctx->serve_hdr;
160 :
161 0 : fd_ip4_hdr_t * ip4 = hdr->ip4;
162 0 : ip4->saddr = src_ip_addr;
163 0 : ip4->daddr = dst_ip_addr;
164 0 : ip4->net_id = fd_ushort_bswap( ctx->net_id++ );
165 0 : ip4->check = 0U;
166 0 : ip4->net_tot_len = fd_ushort_bswap( (ushort)(payload_sz + sizeof(fd_ip4_hdr_t)+sizeof(fd_udp_hdr_t)) );
167 0 : ip4->check = fd_ip4_hdr_check_fast( ip4 );
168 :
169 0 : fd_udp_hdr_t * udp = hdr->udp;
170 0 : udp->net_dport = dst_port;
171 0 : udp->net_len = fd_ushort_bswap( (ushort)(payload_sz + sizeof(fd_udp_hdr_t)) );
172 0 : fd_memcpy( packet+sizeof(fd_ip4_udp_hdrs_t), payload, payload_sz );
173 0 : hdr->udp->check = 0U;
174 :
175 0 : ulong tspub = fd_frag_meta_ts_comp( fd_tickcount() );
176 0 : ulong sig = fd_disco_netmux_sig( dst_ip_addr, dst_port, dst_ip_addr, DST_PROTO_OUTGOING, sizeof(fd_ip4_udp_hdrs_t) );
177 0 : ulong packet_sz = payload_sz + sizeof(fd_ip4_udp_hdrs_t);
178 0 : ulong chunk = ctx->net_out_chunk;
179 0 : fd_stem_publish( stem, ctx->net_out_idx, sig, chunk, packet_sz, 0UL, tsorig, tspub );
180 0 : ctx->net_out_chunk = fd_dcache_compact_next( chunk, packet_sz, ctx->net_out_chunk0, ctx->net_out_wmark );
181 0 : }
182 :
183 : static inline void
184 : handle_pong( ctx_t * ctx,
185 : uchar const * payload,
186 : ulong payload_sz,
187 : uint saddr,
188 0 : ushort sport ) {
189 0 : if( FD_UNLIKELY( payload_sz!=sizeof(uint)+sizeof(fd_repair_pong_t) ) ) return;
190 0 : fd_repair_msg_t const * msg = (fd_repair_msg_t const *)fd_type_pun_const( payload );
191 0 : fd_repair_pong_t const * request = &msg->pong;
192 :
193 0 : if( FD_UNLIKELY( fd_pubkey_eq( &ctx->identity_public_key, &request->from ) ) ) {
194 : /* We've received our own repair request, ignore. */
195 0 : ctx->metrics->fail_own_key++;
196 0 : return;
197 0 : }
198 :
199 0 : if( FD_UNLIKELY( FD_ED25519_SUCCESS!=fd_ed25519_verify( request->hash.uc, 32UL, request->sig, request->from.uc, ctx->sha512 ) ) ) {
200 : /* Invalid signature, ignore. */
201 0 : ctx->metrics->fail_sigverify_request++;
202 0 : return;
203 0 : }
204 :
205 : /* Verify that the pong hash corresponds to the token we would have issued
206 : to this (pubkey, address) under either the current or previous rotating
207 : secret. */
208 0 : if( FD_UNLIKELY( !fd_rserve_pong_token_verify( ctx->rserve, request->hash.uc, &request->from, saddr, sport ) ) ) {
209 0 : ctx->metrics->fail_invalid_token++;
210 0 : return;
211 0 : }
212 :
213 0 : fd_rserve_t * rserve = ctx->rserve;
214 0 : ping_cache_key_t key[1];
215 0 : memset( key, 0, sizeof(ping_cache_key_t) );
216 0 : key->pubkey = request->from;
217 0 : key->ip4 = saddr;
218 0 : key->port = sport;
219 :
220 0 : ping_cache_entry_t * entry = ping_map_ele_query( rserve->ping_map, key, NULL, rserve->ping_pool );
221 :
222 0 : if( FD_LIKELY( !entry ) ) {
223 : /* New entry, evict LRU if pool is full. */
224 0 : if( FD_UNLIKELY( !ping_pool_free( rserve->ping_pool ) ) ) {
225 0 : ping_cache_entry_t * victim = ping_dlist_ele_pop_head( rserve->ping_dlist, rserve->ping_pool );
226 0 : ping_map_ele_remove_fast( rserve->ping_map, victim, rserve->ping_pool );
227 0 : ping_pool_ele_release( rserve->ping_pool, victim );
228 0 : ctx->metrics->ping_cache_entries--;
229 0 : ctx->metrics->ping_cache_evictions++;
230 0 : }
231 0 : entry = ping_pool_ele_acquire( rserve->ping_pool );
232 0 : entry->key = *key;
233 0 : ping_map_ele_insert( rserve->ping_map, entry, rserve->ping_pool );
234 0 : ctx->metrics->ping_cache_entries++;
235 0 : } else {
236 : /* Existing entry, move to tail. */
237 0 : ping_dlist_ele_remove( rserve->ping_dlist, entry, rserve->ping_pool );
238 0 : }
239 :
240 0 : entry->timestamp = (ulong)fd_log_wallclock();
241 0 : ping_dlist_ele_push_tail( rserve->ping_dlist, entry, rserve->ping_pool );
242 0 : return;
243 0 : }
244 :
245 : static inline void
246 : handle_net_request( ctx_t * ctx,
247 : fd_stem_context_t * stem,
248 : uchar const * payload,
249 : ulong payload_sz,
250 : fd_udp_hdr_t * udp,
251 0 : fd_ip4_hdr_t * ip4 ) {
252 0 : if( FD_UNLIKELY( payload_sz<4UL ) ) {
253 0 : ctx->metrics->received_malformed_count[ FD_METRICS_ENUM_RSERVE_MALFORMED_TYPES_V_TOO_SMALL_IDX ]++;
254 0 : return;
255 0 : }
256 0 : uint tag = FD_LOAD( uint, payload );
257 0 : ulong msg_sz = payload_sz-4UL;
258 :
259 0 : if( FD_UNLIKELY( tag==FD_REPAIR_KIND_PONG ) ) {
260 0 : ctx->metrics->received_request_count[ request_metric_index[tag] ]++;
261 0 : ctx->metrics->received_request_bytes += payload_sz;
262 0 : handle_pong( ctx, payload, payload_sz, ip4->saddr, udp->net_sport );
263 0 : return;
264 0 : }
265 0 : if( FD_UNLIKELY( tag!=FD_REPAIR_KIND_SHRED &&
266 0 : tag!=FD_REPAIR_KIND_HIGHEST_SHRED &&
267 0 : tag!=FD_REPAIR_KIND_ORPHAN ) ) {
268 0 : if( tag==FD_REPAIR_KIND_PING ) ctx->metrics->received_malformed_count[ FD_METRICS_ENUM_RSERVE_MALFORMED_TYPES_V_PING_IDX ]++;
269 0 : else if( tag==FD_REPAIR_KIND_ANCESTOR_HASHES ) ctx->metrics->received_malformed_count[ FD_METRICS_ENUM_RSERVE_MALFORMED_TYPES_V_ANCESTOR_HASHES_IDX ]++;
270 0 : else ctx->metrics->received_malformed_count[ FD_METRICS_ENUM_RSERVE_MALFORMED_TYPES_V_UNKNOWN_TAG_IDX ]++;
271 0 : return;
272 0 : }
273 :
274 : /* Validate exact message size for each request type. We must check
275 : this before constructing the signable payload to avoid OOB reads. */
276 0 : if( FD_UNLIKELY( tag==FD_REPAIR_KIND_ORPHAN ) ) {
277 0 : if( FD_UNLIKELY( msg_sz!=sizeof(fd_repair_orphan_req_t) ) ) {
278 0 : ctx->metrics->received_malformed_count[ FD_METRICS_ENUM_RSERVE_MALFORMED_TYPES_V_WRONG_SIZE_IDX ]++;
279 0 : return;
280 0 : }
281 0 : } else {
282 0 : if( FD_UNLIKELY( msg_sz!=sizeof(fd_repair_shred_req_t) ) ) {
283 0 : ctx->metrics->received_malformed_count[ FD_METRICS_ENUM_RSERVE_MALFORMED_TYPES_V_WRONG_SIZE_IDX ]++;
284 0 : return;
285 0 : }
286 0 : }
287 :
288 0 : ctx->metrics->received_request_count[ request_metric_index[tag] ]++;
289 0 : ctx->metrics->received_request_bytes += payload_sz;
290 :
291 0 : fd_repair_req_header_t header[1];
292 0 : memcpy( header, payload+4UL, sizeof(fd_repair_req_header_t) );
293 :
294 0 : if( FD_UNLIKELY( !fd_pubkey_eq( &ctx->identity_public_key, &header->to ) ) ) {
295 0 : ctx->metrics->fail_not_for_us++;
296 0 : return;
297 0 : }
298 0 : if( FD_UNLIKELY( fd_pubkey_eq( &ctx->identity_public_key, &header->from ) ) ) {
299 0 : ctx->metrics->fail_own_key++;
300 0 : return;
301 0 : }
302 :
303 0 : long current = FD_NANOSEC_TO_MILLI( fd_log_wallclock() );
304 0 : long ts_diff = current - (long)header->ts;
305 0 : if( FD_UNLIKELY( ts_diff < 0L ) ) ts_diff = -ts_diff;
306 0 : if( FD_UNLIKELY( ts_diff > FD_RSERVE_SIGNED_REPAIR_WINDOW ) ) {
307 0 : ctx->metrics->fail_outdated++;
308 0 : return;
309 0 : }
310 :
311 : /* Verify the signature. */
312 :
313 : /* The largest signable payload size is 96 bytes, that being
314 : 160-64=96, as the signature itself is not included. */
315 0 : uchar signable[ 96 ];
316 0 : uchar signable_sz = tag==FD_REPAIR_KIND_ORPHAN ? 88 : 96;
317 0 : fd_memcpy( signable, payload, 4 );
318 0 : fd_memcpy( signable+4UL, payload+68UL, signable_sz-4 );
319 :
320 0 : if( FD_UNLIKELY( FD_ED25519_SUCCESS!=fd_ed25519_verify( signable, signable_sz, header->sig, header->from.uc, ctx->sha512 ) ) ) {
321 0 : ctx->metrics->fail_sigverify_request++;
322 0 : return;
323 0 : }
324 :
325 : /* Check whether we've heard a pong response from this peer at this
326 : exact source address. Keying on (pubkey, address) means a peer that
327 : ponged from one address cannot have repair responses redirected to
328 : a spoofed source address. */
329 0 : ping_cache_key_t key[1];
330 0 : memset( key, 0, sizeof(ping_cache_key_t) );
331 0 : key->pubkey = header->from;
332 0 : key->ip4 = ip4->saddr;
333 0 : key->port = udp->net_sport;
334 0 : ping_cache_entry_t * entry = ping_map_ele_query( ctx->rserve->ping_map, key, NULL, ctx->rserve->ping_pool );
335 0 : if( FD_LIKELY( entry ) ) {
336 0 : switch( tag ) {
337 0 : case FD_REPAIR_KIND_SHRED:
338 0 : case FD_REPAIR_KIND_HIGHEST_SHRED: {
339 0 : fd_repair_shred_req_t msg[1];
340 0 : memcpy( msg, payload+4UL, sizeof(fd_repair_shred_req_t) );
341 :
342 0 : ulong slot = msg->slot;
343 0 : ulong shred_idx = msg->shred_idx;
344 :
345 0 : if( FD_UNLIKELY( shred_idx>=ctx->max_shreds_per_block ) ) {
346 0 : ctx->metrics->fail_invalid_shred_idx++;
347 0 : return;
348 0 : }
349 :
350 0 : uchar payload[ FD_SHRED_MAX_SZ+sizeof(uint) ];
351 0 : int len;
352 0 : for( ulong retry=0UL;; retry++ ) {
353 0 : if( tag==FD_REPAIR_KIND_SHRED ) {
354 0 : len = fd_store_disk_query( ctx->store, ctx->disk_fd, slot, (uint)shred_idx, payload );
355 0 : } else {
356 0 : len = fd_store_disk_query_highest( ctx->store, ctx->disk_fd, slot, (uint)shred_idx, payload );
357 0 : }
358 0 : if( FD_LIKELY( len!=FD_STORE_DISK_QUERY_BUSY || retry==7UL ) ) break;
359 0 : for( ulong pause=0UL; pause<(1UL<<retry); pause++ ) FD_SPIN_PAUSE();
360 0 : }
361 0 : if( FD_UNLIKELY( len<0 ) ) {
362 0 : if( len==FD_STORE_DISK_QUERY_BUSY ) ctx->metrics->disk_read_busy++;
363 0 : else if( len==FD_STORE_DISK_QUERY_SCAN_LIMIT) ctx->metrics->disk_read_scan_limit++;
364 0 : else ctx->metrics->disk_read_miss++;
365 0 : ctx->metrics->missed_pkt_types[ response_metric_index[tag] ]++;
366 0 : return;
367 0 : }
368 0 : ctx->metrics->disk_read_success++;
369 0 : ctx->metrics->disk_read_bytes += (ulong)len;
370 :
371 0 : fd_memcpy( payload+len, &header->nonce, sizeof(uint) );
372 0 : send_packet( ctx, stem, ip4->saddr, udp->net_sport, ip4->daddr, payload, (ulong)len+sizeof(uint), fd_frag_meta_ts_comp( fd_tickcount() ) );
373 0 : ctx->metrics->sent_pkt_types[ response_metric_index[tag] ]++;
374 0 : return;
375 0 : }
376 0 : case FD_REPAIR_KIND_ORPHAN: {
377 : /* Orphan repair works by giving us a "root" slot to start at,
378 : and has us walk back through the parent slots sending the
379 : highest shred of each slot.
380 : We may send up to FD_RSERVE_MAX_ORPHAN_SLOTS of these shreds
381 : (including the root one). */
382 0 : fd_repair_orphan_req_t msg[1];
383 0 : memcpy( msg, payload+4UL, sizeof(fd_repair_orphan_req_t) );
384 :
385 0 : ulong current = msg->slot;
386 0 : for( uint i=0; i<FD_RSERVE_MAX_ORPHAN_SLOTS; i++ ) {
387 0 : uchar payload[ FD_SHRED_MAX_SZ+sizeof(uint) ];
388 0 : int len;
389 0 : for( ulong retry=0UL;; retry++ ) {
390 0 : len = fd_store_disk_query_highest( ctx->store, ctx->disk_fd, current, 0, payload );
391 0 : if( FD_LIKELY( len!=FD_STORE_DISK_QUERY_BUSY || retry==7UL ) ) break;
392 0 : for( ulong pause=0UL; pause<(1UL<<retry); pause++ ) FD_SPIN_PAUSE();
393 0 : }
394 0 : if( FD_UNLIKELY( len<0 ) ) {
395 0 : if( len==FD_STORE_DISK_QUERY_BUSY ) ctx->metrics->disk_read_busy++;
396 0 : else if( len==FD_STORE_DISK_QUERY_SCAN_LIMIT) ctx->metrics->disk_read_scan_limit++;
397 0 : else ctx->metrics->disk_read_miss++;
398 0 : ctx->metrics->missed_pkt_types[ FD_METRICS_ENUM_RSERVE_SENT_RESPONSE_TYPES_V_ORPHAN_IDX ]++;
399 0 : return;
400 0 : }
401 0 : ctx->metrics->disk_read_success++;
402 0 : ctx->metrics->disk_read_bytes += (ulong)len;
403 0 : fd_shred_t const * shred = (fd_shred_t const *)fd_type_pun_const( payload );
404 0 : memcpy( payload+len, &header->nonce, sizeof(uint) );
405 0 : send_packet( ctx, stem, ip4->saddr, udp->net_sport, ip4->daddr, payload, (ulong)len+sizeof(uint), fd_frag_meta_ts_comp( fd_tickcount() ) );
406 0 : ctx->metrics->sent_pkt_types[ FD_METRICS_ENUM_RSERVE_SENT_RESPONSE_TYPES_V_ORPHAN_IDX ]++;
407 0 : ushort parent_off = shred->data.parent_off;
408 : /* fd_shred_parse ensures that parent_off will be 0 if and only if slot is 0 */
409 0 : if( FD_UNLIKELY( parent_off==0 ) ) break;
410 0 : current = current - parent_off;
411 0 : }
412 0 : break;
413 0 : }
414 0 : }
415 0 : } else {
416 0 : ctx->metrics->fail_ping_cache_lookup++;
417 :
418 : /* Derive a ping token bound to this peer's pubkey and the source
419 : address the request arrived from. The peer can only produce a
420 : valid pong if it actually receives this ping at that address. */
421 0 : uchar token[ 32UL ];
422 0 : fd_rserve_ping_token( ctx->rserve, token, &header->from, ip4->saddr, udp->net_sport );
423 :
424 : /* Sign the token. */
425 0 : uchar signature[ 64UL ];
426 0 : fd_keyguard_client_sign( ctx->keyguard_client, signature, token, 32UL, FD_KEYGUARD_SIGN_TYPE_ED25519 );
427 :
428 0 : fd_repair_ping_t msg[ 1 ];
429 0 : msg->kind = FD_REPAIR_KIND_PING;
430 0 : msg->ping.from = ctx->identity_public_key;
431 0 : memcpy( msg->ping.sig, signature, 64 );
432 0 : memcpy( msg->ping.hash.uc, token, 32 );
433 :
434 : /* Send the ping packet back to the source. */
435 0 : send_packet( ctx, stem, ip4->saddr, udp->net_sport, ip4->daddr, (uchar const *)fd_type_pun_const( msg ), sizeof(fd_repair_ping_t), fd_frag_meta_ts_comp( fd_tickcount() ) );
436 0 : ctx->metrics->sent_pkt_types[ FD_METRICS_ENUM_RSERVE_SENT_RESPONSE_TYPES_V_PING_IDX ]++;
437 0 : }
438 0 : }
439 :
440 :
441 : static inline int
442 : returnable_frag( ctx_t * ctx,
443 : ulong in_idx,
444 : ulong seq FD_PARAM_UNUSED,
445 : ulong sig,
446 : ulong chunk,
447 : ulong sz,
448 : ulong ctl,
449 : ulong tsorig FD_PARAM_UNUSED,
450 : ulong tspub FD_PARAM_UNUSED,
451 0 : fd_stem_context_t * stem ) {
452 0 : uint in_kind = ctx->in_kind[ in_idx ];
453 0 : in_ctx_t const * in_ctx = &ctx->in_links[ in_idx ];
454 :
455 0 : switch( in_kind ) {
456 0 : case IN_KIND_NET: {
457 0 : if( FD_UNLIKELY( ctx->halt_signing ) ) return 1;
458 0 : if( fd_disco_netmux_sig_proto( sig )!=DST_PROTO_RSERVE ) return 0;
459 :
460 0 : uchar const * buffer = fd_net_rx_translate_frag( &in_ctx->net_rx, chunk, ctl, sz );
461 0 : uchar * payload; ulong payload_sz;
462 0 : fd_udp_hdr_t * udp;
463 0 : fd_ip4_hdr_t * ip4;
464 0 : if( FD_UNLIKELY( !fd_ip4_udp_hdr_strip( buffer, sz, &payload, &payload_sz, NULL, &ip4, &udp ) ) ) {
465 0 : FD_LOG_WARNING(( "rserve: malformed packet (sz=%lu)", sz ));
466 0 : return 0;
467 0 : }
468 0 : handle_net_request( ctx, stem, payload, payload_sz, udp, ip4 );
469 0 : return 0;
470 0 : }
471 0 : case IN_KIND_SIGN: return 0; /* handled internally by keyguard_client */
472 0 : case IN_KIND_SHRED: {
473 0 : int shred_result = fd_shred_sig_res( sig );
474 0 : if( FD_UNLIKELY( fd_shred_sig_src( sig )>SHRED_SIG_SRC_BAD_REPAIR ||
475 0 : (shred_result!=SHRED_SIG_RESULT_OKAY && shred_result!=SHRED_SIG_RESULT_COMPLETES) ) ) return 0;
476 0 : if( FD_UNLIKELY( sz!=sizeof(fd_shred_base_t) || chunk<in_ctx->chunk0 || chunk>in_ctx->wmark ) )
477 0 : FD_LOG_ERR(( "shred_out chunk %lu %lu corrupt, not in range [%lu,%lu]", chunk, sz, in_ctx->chunk0, in_ctx->wmark ));
478 :
479 0 : fd_shred_base_t const * msg = fd_chunk_to_laddr_const( in_ctx->mem, chunk );
480 0 : fd_shred_t const * shred = &msg->shred;
481 0 : if( FD_UNLIKELY( !fd_shred_is_data( fd_shred_type( shred->variant ) ) ) ) return 0;
482 :
483 0 : long dt = -fd_tickcount();
484 0 : int result = fd_store_disk_insert( ctx->store, ctx->disk_fd, shred );
485 0 : dt += fd_tickcount();
486 0 : fd_histf_sample( ctx->metrics->disk_write_timing, (ulong)dt );
487 0 : if( FD_LIKELY( result==FD_STORE_DISK_INSERT_SUCCESS ) ) {
488 0 : ctx->metrics->disk_inserted++;
489 0 : ctx->metrics->disk_write_bytes += sizeof(fd_shredb_entry_t);
490 0 : } else ctx->metrics->disk_write_failed++;
491 0 : return 0;
492 0 : }
493 0 : default: FD_LOG_ERR(( "unexpected input kind (%u)", in_kind ));
494 0 : }
495 0 : }
496 :
497 : static inline void
498 : before_credit( ctx_t * ctx,
499 : fd_stem_context_t * stem FD_PARAM_UNUSED,
500 0 : int * charge_busy ) {
501 0 : fd_store_fec_spill_stats_t spill[1];
502 0 : if( FD_UNLIKELY( fd_store_fec_data_preevict( ctx->store, ctx->disk_fd, spill ) ) ) {
503 0 : fd_histf_sample( ctx->metrics->fec_preevict_timing, spill->write_ticks );
504 0 : ctx->metrics->fec_preevict_write_cnt += spill->write_cnt;
505 0 : ctx->metrics->fec_preevict_write_bytes += spill->write_bytes;
506 0 : *charge_busy = 1;
507 0 : return;
508 0 : }
509 0 : if( FD_UNLIKELY( fd_store_disk_maintain( ctx->store, ctx->disk_fd ) ) ) *charge_busy = 1;
510 0 : }
511 :
512 : static inline void
513 0 : during_housekeeping( ctx_t * ctx ) {
514 0 : if( FD_UNLIKELY( fd_keyswitch_state_query( ctx->keyswitch )==FD_KEYSWITCH_STATE_UNHALT_PENDING ) ) {
515 0 : FD_LOG_DEBUG(( "keyswitch: unhalting" ));
516 0 : FD_DCHECK_CRIT( ctx->halt_signing, "state machine corruption" );
517 0 : fd_memcpy( ctx->identity_public_key.uc, ctx->keyswitch->bytes, sizeof(fd_pubkey_t) );
518 0 : ctx->halt_signing = 0;
519 0 : fd_keyswitch_state( ctx->keyswitch, FD_KEYSWITCH_STATE_COMPLETED );
520 0 : }
521 :
522 0 : if( FD_UNLIKELY( fd_keyswitch_state_query( ctx->keyswitch )==FD_KEYSWITCH_STATE_SWITCH_PENDING ) ) {
523 0 : ctx->halt_signing = 1;
524 0 : fd_keyswitch_state( ctx->keyswitch, FD_KEYSWITCH_STATE_COMPLETED );
525 0 : }
526 :
527 0 : fd_rserve_t * rserve = ctx->rserve;
528 0 : ulong now_ns = (ulong)fd_log_wallclock();
529 0 : fd_rserve_maybe_rotate( rserve, now_ns );
530 :
531 : /* Evict expired entries from the head (oldest) of the LRU list. */
532 0 : while( !ping_dlist_is_empty( rserve->ping_dlist, rserve->ping_pool ) ) {
533 0 : ping_cache_entry_t * head = ping_dlist_ele_peek_head( rserve->ping_dlist, rserve->ping_pool );
534 0 : if( FD_LIKELY( now_ns-head->timestamp <= FD_RSERVE_PING_CACHE_TTL_NS ) ) break;
535 0 : ping_dlist_ele_pop_head( rserve->ping_dlist, rserve->ping_pool );
536 0 : ping_map_ele_remove_fast( rserve->ping_map, head, rserve->ping_pool );
537 0 : ping_pool_ele_release( rserve->ping_pool, head );
538 0 : ctx->metrics->ping_cache_entries--;
539 0 : }
540 0 : }
541 :
542 : static inline void
543 0 : metrics_write( ctx_t * ctx ) {
544 0 : fd_store_fec_cache_stats_t cache_stats[1] = {{0}};
545 0 : if( FD_LIKELY( ctx->store ) ) {
546 0 : fd_store_disk_stats_t stats[1];
547 0 : if( FD_LIKELY( !fd_store_disk_stats_query( ctx->store, stats ) ) ) {
548 0 : ctx->metrics->shreds_current = stats->shred_cnt;
549 0 : ctx->metrics->disk_current_bytes = stats->current_bytes;
550 0 : ctx->metrics->disk_allocated_bytes = stats->allocated_bytes;
551 0 : }
552 0 : fd_store_fec_cache_stats_query( ctx->store, cache_stats );
553 0 : }
554 :
555 0 : FD_MCNT_ENUM_COPY( RSERVE, RECEIVED_REQUEST_COUNT, ctx->metrics->received_request_count );
556 0 : FD_MCNT_SET( RSERVE, RECEIVED_REQUEST_BYTES, ctx->metrics->received_request_bytes );
557 0 : FD_MCNT_ENUM_COPY( RSERVE, RECEIVED_MALFORMED_COUNT, ctx->metrics->received_malformed_count );
558 :
559 0 : FD_MCNT_SET( RSERVE, TOTAL_PKT_COUNT, ctx->metrics->send_pkt_cnt );
560 0 : FD_MCNT_ENUM_COPY( RSERVE, SENT_RESPONSE_TYPES, ctx->metrics->sent_pkt_types );
561 0 : FD_MCNT_SET( RSERVE, SENT_RESPONSE_BYTES, ctx->metrics->sent_response_bytes );
562 :
563 0 : FD_MCNT_ENUM_COPY( RSERVE, MISSED_RESPONSE_TYPES, ctx->metrics->missed_pkt_types );
564 0 : FD_MCNT_SET( RSERVE, FAILED_SIGVERIFY, ctx->metrics->fail_sigverify_request );
565 0 : FD_MCNT_SET( RSERVE, FAILED_OWN_KEY, ctx->metrics->fail_own_key );
566 0 : FD_MCNT_SET( RSERVE, FAILED_INVALID_TOKEN, ctx->metrics->fail_invalid_token );
567 0 : FD_MCNT_SET( RSERVE, FAILED_NOT_FOR_US, ctx->metrics->fail_not_for_us );
568 0 : FD_MCNT_SET( RSERVE, FAILED_OUTDATED, ctx->metrics->fail_outdated );
569 0 : FD_MCNT_SET( RSERVE, FAILED_INVALID_SHRED_INDEX, ctx->metrics->fail_invalid_shred_idx );
570 0 : FD_MCNT_SET( RSERVE, FAILED_PING_CACHE_LOOKUP, ctx->metrics->fail_ping_cache_lookup );
571 0 : FD_MCNT_SET( RSERVE, DISK_READ_BUSY, ctx->metrics->disk_read_busy );
572 0 : FD_MCNT_SET( RSERVE, DISK_READ_MISS, ctx->metrics->disk_read_miss );
573 0 : FD_MCNT_SET( RSERVE, DISK_READ_SCAN_LIMIT, ctx->metrics->disk_read_scan_limit );
574 0 : FD_MCNT_SET( RSERVE, DISK_READ_SUCCESS, ctx->metrics->disk_read_success );
575 0 : FD_MCNT_SET( RSERVE, DISK_READ_BYTES, ctx->metrics->disk_read_bytes );
576 0 : FD_MGAUGE_SET( RSERVE, SHREDS_CURRENT, ctx->metrics->shreds_current );
577 0 : FD_MGAUGE_SET( RSERVE, DISK_CURRENT_BYTES, ctx->metrics->disk_current_bytes );
578 0 : FD_MGAUGE_SET( RSERVE, DISK_ALLOCATED_BYTES, ctx->metrics->disk_allocated_bytes );
579 0 : FD_MHIST_COPY( RSERVE, DISK_WRITE_SECONDS, ctx->metrics->disk_write_timing );
580 0 : FD_MCNT_SET ( RSERVE, DISK_SHRED_INSERTED, ctx->metrics->disk_inserted );
581 0 : FD_MCNT_SET ( RSERVE, DISK_WRITE_FAILED, ctx->metrics->disk_write_failed );
582 0 : FD_MCNT_SET ( RSERVE, DISK_WRITE_BYTES, ctx->metrics->disk_write_bytes );
583 0 : FD_MHIST_COPY( RSERVE, FEC_PREEVICT_WRITE_SECONDS, ctx->metrics->fec_preevict_timing );
584 0 : FD_MCNT_SET ( RSERVE, FEC_PREEVICT_WRITE, ctx->metrics->fec_preevict_write_cnt );
585 0 : FD_MCNT_SET ( RSERVE, FEC_PREEVICT_WRITE_BYTES, ctx->metrics->fec_preevict_write_bytes );
586 0 : FD_MGAUGE_SET( RSERVE, FEC_CACHE_FREE, cache_stats->free_cnt );
587 0 : FD_MGAUGE_SET( RSERVE, FEC_CACHE_MAX, cache_stats->max );
588 0 : FD_MGAUGE_SET( RSERVE, FEC_CACHE_TARGET, cache_stats->target );
589 0 : FD_MGAUGE_SET( RSERVE, FEC_CACHE_LOW_WATER, cache_stats->low_water );
590 :
591 0 : FD_MCNT_SET( RSERVE, PING_CACHE_ENTRIES, ctx->metrics->ping_cache_entries );
592 0 : FD_MCNT_SET( RSERVE, PING_CACHE_EVICTIONS, ctx->metrics->ping_cache_evictions );
593 0 : }
594 :
595 : static void
596 : privileged_init( fd_topo_t const * topo,
597 0 : fd_topo_tile_t const * tile ) {
598 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
599 :
600 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
601 0 : ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(ctx_t), sizeof(ctx_t) );
602 :
603 0 : FD_TEST( fd_rng_secure( &ctx->seed, sizeof(ulong) ) );
604 0 : FD_TEST( fd_rng_secure( ctx->rserve_secret, sizeof(ctx->rserve_secret) ) );
605 :
606 0 : ctx->disk_fd = -1;
607 0 : ulong store_obj_id = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "store" );
608 0 : if( FD_LIKELY( store_obj_id!=ULONG_MAX ) ) {
609 0 : fd_store_t * store = fd_store_join( fd_topo_obj_laddr( topo, store_obj_id ) );
610 0 : FD_TEST( store );
611 0 : ctx->disk_fd = FD_STORE_FD_RW;
612 0 : if( FD_UNLIKELY( fcntl( ctx->disk_fd, F_GETFD )<0 ) )
613 0 : FD_LOG_ERR(( "store file descriptor was not inherited (%i-%s)", errno, fd_io_strerror( errno ) ));
614 0 : }
615 :
616 0 : uchar const * identity_public_key = fd_keyload_load( tile->rserve.identity_key_path, /* pubkey only: */ 1 );
617 0 : fd_memcpy( ctx->identity_public_key.uc, identity_public_key, sizeof(fd_pubkey_t) );
618 0 : }
619 :
620 : static void
621 : unprivileged_init( fd_topo_t const * topo,
622 0 : fd_topo_tile_t const * tile ) {
623 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
624 :
625 0 : ulong ping_cache_entries = tile->rserve.ping_cache_entries;
626 :
627 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
628 0 : ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(ctx_t), sizeof(ctx_t) );
629 0 : ctx->rserve = FD_SCRATCH_ALLOC_APPEND( l, fd_rserve_align(), fd_rserve_footprint( ping_cache_entries ) );
630 0 : FD_TEST( FD_SCRATCH_ALLOC_FINI( l, scratch_align() )==(ulong)scratch + scratch_footprint( tile ) );
631 :
632 0 : ctx->store = NULL;
633 0 : ulong store_obj_id = fd_pod_queryf_ulong( topo->props, ULONG_MAX, "store" );
634 0 : if( FD_LIKELY( store_obj_id!=ULONG_MAX ) ) {
635 0 : ctx->store = fd_store_join( fd_topo_obj_laddr( topo, store_obj_id ) );
636 0 : FD_TEST( ctx->store );
637 0 : }
638 0 : if( FD_UNLIKELY( !ctx->store || !fd_store_has_disk( ctx->store ) ) ) {
639 0 : FD_LOG_ERR(( "rserve requires an enabled store disk layer" ));
640 0 : }
641 0 : if( FD_UNLIKELY( ctx->disk_fd<0 ) ) {
642 0 : FD_LOG_ERR(( "rserve could not open the store disk file" ));
643 0 : }
644 :
645 0 : ctx->max_shreds_per_block = tile->rserve.max_shreds_per_block;
646 0 : ctx->rserve = fd_rserve_join ( fd_rserve_new( ctx->rserve, ping_cache_entries, ctx->seed, ctx->rserve_secret ) );
647 0 : ctx->keyswitch = fd_keyswitch_join( fd_topo_obj_laddr( topo, tile->id_keyswitch_obj_id ) );
648 0 : FD_TEST( ctx->keyswitch );
649 :
650 0 : fd_memset( ctx->metrics, 0, sizeof(ctx->metrics) );
651 0 : fd_histf_join( fd_histf_new( ctx->metrics->disk_write_timing,
652 0 : FD_MHIST_SECONDS_MIN( RSERVE, DISK_WRITE_SECONDS ),
653 0 : FD_MHIST_SECONDS_MAX( RSERVE, DISK_WRITE_SECONDS ) ) );
654 0 : fd_histf_join( fd_histf_new( ctx->metrics->fec_preevict_timing,
655 0 : FD_MHIST_SECONDS_MIN( RSERVE, FEC_PREEVICT_WRITE_SECONDS ),
656 0 : FD_MHIST_SECONDS_MAX( RSERVE, FEC_PREEVICT_WRITE_SECONDS ) ) );
657 0 : FD_MGAUGE_SET( RSERVE, SHREDS_MAX, ctx->store ? ctx->store->disk_max_shreds : 0UL );
658 :
659 0 : ctx->halt_signing = 0;
660 0 : ctx->net_id = (ushort)0;
661 0 : fd_ip4_udp_hdr_init( ctx->serve_hdr, FD_RSERVE_MAX_PACKET_SIZE, 0, tile->rserve.repair_serve_listen_port );
662 0 : fd_sha512_new( ctx->sha512 );
663 :
664 0 : ulong sign_in_idx = fd_topo_find_tile_in_link ( topo, tile, "sign_rserve", tile->kind_id );
665 0 : ulong sign_out_idx = fd_topo_find_tile_out_link( topo, tile, "rserve_sign", tile->kind_id );
666 0 : FD_TEST( sign_in_idx!=ULONG_MAX );
667 0 : fd_topo_link_t const * sign_in = &topo->links[ tile->in_link_id[ sign_in_idx ] ];
668 0 : fd_topo_link_t const * sign_out = &topo->links[ tile->out_link_id[ sign_out_idx ] ];
669 0 : if( FD_UNLIKELY( !fd_keyguard_client_join( fd_keyguard_client_new( ctx->keyguard_client,
670 0 : sign_out->mcache,
671 0 : sign_out->dcache,
672 0 : sign_in->mcache,
673 0 : sign_in->dcache,
674 0 : sign_out->mtu,
675 0 : sign_in->mtu ) ) ) ) {
676 0 : FD_LOG_ERR(( "failed to construct keyguard" ));
677 0 : }
678 :
679 0 : FD_TEST( tile->in_cnt>=1UL );
680 0 : FD_CHECK_ERR( tile->in_cnt<=MAX_IN_LINKS, "too many input links" );
681 0 : for( ulong in_idx=0UL; in_idx<tile->in_cnt; in_idx++ ) {
682 0 : fd_topo_link_t const * link = &topo->links[ tile->in_link_id[ in_idx ] ];
683 0 : if( 0==strcmp( link->name, "net_rserve" ) ) {
684 0 : ctx->in_kind[ in_idx ] = IN_KIND_NET;
685 0 : fd_net_rx_bounds_init( &ctx->in_links[ in_idx ].net_rx, link->dcache );
686 0 : continue;
687 0 : }
688 0 : else if( 0==strcmp( link->name, "sign_rserve" ) ) ctx->in_kind[ in_idx ] = IN_KIND_SIGN;
689 0 : else if( 0==strcmp( link->name, "shred_out" ) ) ctx->in_kind[ in_idx ] = IN_KIND_SHRED;
690 0 : else FD_LOG_ERR(( "rserve tile has unexpected input link: %s", link->name ));
691 :
692 0 : ctx->in_links[ in_idx ].mem = topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ].wksp;
693 0 : ctx->in_links[ in_idx ].chunk0 = fd_dcache_compact_chunk0( ctx->in_links[ in_idx ].mem, link->dcache );
694 0 : ctx->in_links[ in_idx ].wmark = fd_dcache_compact_wmark ( ctx->in_links[ in_idx ].mem, link->dcache, link->mtu );
695 0 : ctx->in_links[ in_idx ].mtu = link->mtu;
696 0 : }
697 :
698 0 : ctx->net_out_idx = UINT_MAX;
699 0 : for( uint out_idx=0U; out_idx<tile->out_cnt; out_idx++ ) {
700 0 : fd_topo_link_t const * link = &topo->links[ tile->out_link_id[ out_idx ] ];
701 0 : if( 0==strcmp( link->name, "rserve_net" ) ) {
702 0 : if( ctx->net_out_idx!=UINT_MAX ) continue; /* only use the first net link */
703 0 : ctx->net_out_idx = out_idx;
704 0 : ctx->net_out_mem = topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ].wksp;
705 0 : ctx->net_out_chunk0 = fd_dcache_compact_chunk0( ctx->net_out_mem, link->dcache );
706 0 : ctx->net_out_wmark = fd_dcache_compact_wmark( ctx->net_out_mem, link->dcache, link->mtu );
707 0 : ctx->net_out_chunk = ctx->net_out_chunk0;
708 0 : }
709 0 : else if( 0==strcmp( link->name, "rserve_sign" ) ) { /* Handled above for keyguard. */ }
710 0 : else FD_LOG_ERR(( "rserve tile has unexpected output link: %s", link->name ));
711 0 : }
712 0 : if( FD_UNLIKELY( ctx->net_out_idx==UINT_MAX ) ) FD_LOG_ERR(( "Missing rserve_net output link" ));
713 0 : }
714 :
715 : static ulong
716 : populate_allowed_seccomp( fd_topo_t const * topo FD_PARAM_UNUSED,
717 : fd_topo_tile_t const * tile FD_PARAM_UNUSED,
718 : ulong out_cnt,
719 0 : struct sock_filter * out ) {
720 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
721 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
722 0 : ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(ctx_t), sizeof(ctx_t) );
723 0 : populate_sock_filter_policy_fd_rserve_tile( out_cnt, out, (uint)fd_log_private_logfile_fd(), (uint)ctx->disk_fd );
724 0 : return sock_filter_policy_fd_rserve_tile_instr_cnt;
725 0 : }
726 :
727 : static ulong
728 : populate_allowed_fds( fd_topo_t const * topo FD_PARAM_UNUSED,
729 : fd_topo_tile_t const * tile FD_PARAM_UNUSED,
730 : ulong out_fds_cnt,
731 0 : int * out_fds ) {
732 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
733 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
734 0 : ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof(ctx_t), sizeof(ctx_t) );
735 :
736 0 : if( FD_UNLIKELY( out_fds_cnt<3UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
737 :
738 0 : ulong out_cnt = 0UL;
739 0 : out_fds[ out_cnt++ ] = 2; /* stderr */
740 0 : if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
741 0 : out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
742 0 : if( FD_LIKELY( ctx->disk_fd>=0 ) )
743 0 : out_fds[ out_cnt++ ] = ctx->disk_fd;
744 0 : return out_cnt;
745 0 : }
746 :
747 : /* For orphan responses, we may send up to 11 net packets out.
748 : All other implemented codepaths will send at most 1. */
749 0 : #define STEM_BURST FD_RSERVE_MAX_ORPHAN_SLOTS
750 0 : #define STEM_LAZY (64000UL)
751 :
752 0 : #define STEM_CALLBACK_CONTEXT_TYPE ctx_t
753 0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(ctx_t)
754 0 : #define STEM_CALLBACK_DURING_HOUSEKEEPING during_housekeeping
755 0 : #define STEM_CALLBACK_METRICS_WRITE metrics_write
756 0 : #define STEM_CALLBACK_BEFORE_CREDIT before_credit
757 0 : #define STEM_CALLBACK_RETURNABLE_FRAG returnable_frag
758 :
759 : #include "../../disco/stem/fd_stem.c"
760 :
761 : fd_topo_run_tile_t fd_tile_rserve = {
762 : .name = "rserve",
763 : .populate_allowed_seccomp = populate_allowed_seccomp,
764 : .populate_allowed_fds = populate_allowed_fds,
765 : .scratch_align = scratch_align,
766 : .scratch_footprint = scratch_footprint,
767 : .privileged_init = privileged_init,
768 : .unprivileged_init = unprivileged_init,
769 : .run = stem_run,
770 : };
|