Skip to content

Commit 57ee3fd

Browse files
committed
Reintroduce repair ping forwarding
Forwards ping frames arriving at the 'repair ingress' port from the shred tile to the repair tile. This was previously done at the net tile level, but is moved to the shred/repair tiles to keep the network stack clean.
1 parent cd811d5 commit 57ee3fd

File tree

9 files changed

+148
-203
lines changed

9 files changed

+148
-203
lines changed

src/app/shared/commands/get_identity.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ get_identity_cmd_fn( args_t * args FD_PARAM_UNUSED,
3030
fd_topo_join_workspace( &config->topo, &config->topo.workspaces[ shred_wksp_id ], FD_SHMEM_JOIN_MODE_READ_ONLY );
3131

3232
/* Cast to shred context structure */
33-
fd_shred_ctx_t const * shred_ctx = fd_topo_obj_laddr( &config->topo, shred_tile->tile_obj_id );
33+
fd_shred_ctx_hdr_t const * shred_ctx = fd_topo_obj_laddr( &config->topo, shred_tile->tile_obj_id );
3434
if( FD_UNLIKELY( !shred_ctx ) ) {
3535
fd_topo_leave_workspaces( &config->topo );
3636
FD_LOG_ERR(( "Failed to access shred tile object" ));

src/disco/shred/fd_shred_tile.c

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#include "../tiles.h"
1+
#include "fd_shred_tile.h"
22

33
#include "generated/fd_shred_tile_seccomp.h"
44
#include "../../util/pod/fd_pod_format.h"
@@ -172,6 +172,7 @@ typedef struct {
172172
ushort net_id;
173173

174174
int skip_frag;
175+
int ping_frag;
175176

176177
ulong adtl_dests_leader_cnt;
177178
fd_shred_dest_weighted_t adtl_dests_leader [ FD_TOPO_ADTL_DESTS_MAX ];
@@ -349,6 +350,64 @@ before_frag( fd_shred_ctx_t * ctx,
349350
return 0;
350351
}
351352

353+
/* *** Ping forwarding ***
354+
Solana peers use a simple 'ping-pong' to do a primitive form of
355+
address and endpoint validation. (Mainly defeats reflection-like
356+
flood attacks, by allowing receivers to decline flows)
357+
358+
Unfortunately, ping-pong flows that belong to the repair tile reuse
359+
the 'repair intake' port, which ends up at the shred tile. So, the
360+
shred tile must forward pings back to repair. */
361+
362+
static int
363+
forward_ping_prepare(
364+
fd_shred_ctx_t * ctx,
365+
uchar const * buf,
366+
ulong sz
367+
) {
368+
/* Don't do anything if the repair tile doesn't exist in this topology */
369+
if( FD_UNLIKELY( !ctx->repair_out_mem ) ) return 0;
370+
371+
/* Extract IPv4 and UDP info, so downstream can generate a response */
372+
if( FD_UNLIKELY( sz<sizeof(fd_eth_hdr_t)+sizeof(fd_ip4_hdr_t) ) ) return 0;
373+
fd_ip4_hdr_t ip4 = FD_LOAD( fd_ip4_hdr_t, buf+sizeof(fd_eth_hdr_t) );
374+
uint ip4_len = FD_IP4_GET_LEN( ip4 );
375+
if( FD_UNLIKELY( sz<sizeof(fd_eth_hdr_t)+ip4_len+sizeof(fd_udp_hdr_t)+FD_REPAIR_PING_SZ ) ) return 0;
376+
fd_udp_hdr_t udp = FD_LOAD( fd_udp_hdr_t, buf+sizeof(fd_eth_hdr_t)+FD_IP4_GET_LEN( ip4 ) );
377+
378+
/* Write a ping frame to the shred->repair link.
379+
Pings are smaller than the shred_repair MTU, so we always have
380+
space to send a frame. */
381+
FD_STATIC_ASSERT( FD_SHRED_REPAIR_MTU>=sizeof(fd_repair_ping_fwd_t), mtu );
382+
fd_repair_ping_fwd_t * dst = fd_chunk_to_laddr( ctx->repair_out_mem, ctx->repair_out_chunk );
383+
dst->src_ip4 = ip4.saddr;
384+
dst->src_port = fd_ushort_bswap( udp.net_sport );
385+
fd_memcpy( dst->ping, buf, FD_REPAIR_PING_SZ );
386+
return 1;
387+
}
388+
389+
static void
390+
forward_ping_commit(
391+
fd_shred_ctx_t * ctx,
392+
fd_stem_context_t * stem
393+
) {
394+
/* Don't do anything if the repair tile doesn't exist in this topology */
395+
if( FD_UNLIKELY( !ctx->repair_out_mem ) ) return;
396+
397+
/* Commit a previous shred->repair ping forward */
398+
ulong out_idx = ctx->repair_out_idx;
399+
ulong sig = ULONG_MAX; /* repair ping */
400+
ulong chunk = ctx->repair_out_chunk;
401+
ulong sz = sizeof(fd_repair_ping_fwd_t);
402+
ulong ctl = 0UL; /* unused */
403+
ulong tsorig = 0UL; /* TODO forward tsorig from upstream packet */
404+
ulong tspub = fd_frag_meta_ts_comp( fd_tickcount() );
405+
fd_stem_publish( stem, out_idx, sig, chunk, sz, ctl, tsorig, tspub );
406+
407+
/* Wind up for next iteration */
408+
ctx->repair_out_chunk = fd_dcache_compact_next( chunk, sz, ctx->repair_out_chunk0, ctx->repair_out_wmark );
409+
}
410+
352411
static void
353412
during_frag( fd_shred_ctx_t * ctx,
354413
ulong in_idx,
@@ -635,6 +694,16 @@ during_frag( fd_shred_ctx_t * ctx,
635694
uchar const * dcache_entry = fd_net_rx_translate_frag( &ctx->in[ in_idx ].net_rx, chunk, ctl, sz );
636695
ulong hdr_sz = fd_disco_netmux_sig_hdr_sz( sig );
637696
FD_TEST( hdr_sz <= sz ); /* Should be ensured by the net tile */
697+
698+
/* Ping traffic generated by the repair tile can end up with the
699+
shred tile ~ forward it. */
700+
if( FD_UNLIKELY( (sz-hdr_sz)==FD_REPAIR_PING_SZ ) ) {
701+
ctx->ping_frag = !!forward_ping_prepare( ctx, dcache_entry, sz );
702+
return;
703+
} else {
704+
ctx->ping_frag = 0;
705+
}
706+
638707
fd_shred_t const * shred = fd_shred_parse( dcache_entry+hdr_sz, sz-hdr_sz );
639708
if( FD_UNLIKELY( !shred ) ) {
640709
ctx->skip_frag = 1;
@@ -808,6 +877,11 @@ after_frag( fd_shred_ctx_t * ctx,
808877
ulong fanout = 200UL; /* Default Agave's DATA_PLANE_FANOUT = 200UL */
809878

810879
if( FD_LIKELY( ctx->in_kind[ in_idx ]==IN_KIND_NET ) ) {
880+
if( FD_UNLIKELY( ctx->ping_frag ) ) {
881+
forward_ping_commit( ctx, stem );
882+
return;
883+
}
884+
811885
uchar * shred_buffer = ctx->shred_buffer;
812886
ulong shred_buffer_sz = ctx->shred_buffer_sz;
813887

src/disco/shred/fd_shred_tile.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,30 @@
44
#include "../tiles.h"
55
#include "../../flamenco/types/fd_types_custom.h"
66

7+
/* FD_REPAIR_PING_SZ is the UDP payload size of a 'ping'-related packet.
8+
These incoming packets are forwarded to the repair tile. */
9+
#define FD_REPAIR_PING_SZ (132UL)
10+
11+
struct fd_repair_ping_fwd {
12+
uint src_ip4;
13+
uint dst_ip4;
14+
ushort src_port;
15+
16+
/* FIXME: Just have a wire-format struct here for the ping frame.
17+
This is currently not possible due to use of fd_types, which is not
18+
guaranteed to have the same in-memory format as the wire format. */
19+
uchar ping[ FD_REPAIR_PING_SZ ];
20+
};
21+
22+
typedef struct fd_repair_ping_fwd fd_repair_ping_fwd_t;
23+
724
/* Forward declarations */
825
typedef struct fd_fec_resolver fd_fec_resolver_t;
926
typedef struct fd_keyswitch_private fd_keyswitch_t;
1027
typedef struct fd_keyguard_client fd_keyguard_client_t;
1128

12-
/* Shred tile context structure */
29+
/* Part of the shred tile context struct
30+
FIXME remove this and just use fd_shred_ctx_t everywhere */
1331
typedef struct {
1432
fd_shredder_t * shredder;
1533
fd_fec_resolver_t * resolver;
@@ -27,6 +45,6 @@ typedef struct {
2745
fd_keyswitch_t * keyswitch;
2846
fd_keyguard_client_t keyguard_client[1];
2947
/* ... rest of the structure members ... */
30-
} fd_shred_ctx_t;
48+
} fd_shred_ctx_hdr_t;
3149

3250
#endif /* HEADER_fd_src_disco_shred_fd_shred_tile_h */

src/discof/repair/fd_repair_tile.c

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define _GNU_SOURCE
33

44
#include "../../disco/topo/fd_topo.h"
5+
#include "../../disco/shred/fd_shred_tile.h"
56
#include "generated/fd_repair_tile_seccomp.h"
67

78
#include "../../flamenco/repair/fd_repair.h"
@@ -253,14 +254,12 @@ handle_new_cluster_contact_info( fd_repair_tile_ctx_t * ctx,
253254
}
254255
}
255256

256-
ulong
257-
fd_repair_handle_ping( fd_repair_tile_ctx_t * repair_tile_ctx,
258-
fd_repair_t * glob,
259-
fd_gossip_ping_t const * ping,
260-
fd_gossip_peer_addr_t const * peer_addr FD_PARAM_UNUSED,
261-
uint self_ip4_addr FD_PARAM_UNUSED,
262-
uchar * msg_buf,
263-
ulong msg_buf_sz ) {
257+
static ulong
258+
fd_repair_handle_ping( fd_repair_tile_ctx_t * repair_tile_ctx,
259+
fd_repair_t * glob,
260+
fd_gossip_ping_t const * ping,
261+
uchar * msg_buf,
262+
ulong msg_buf_sz ) {
264263
fd_repair_protocol_t protocol;
265264
fd_repair_protocol_new_disc(&protocol, fd_repair_protocol_enum_pong);
266265
fd_gossip_ping_t * pong = &protocol.inner.pong;
@@ -286,8 +285,22 @@ fd_repair_handle_ping( fd_repair_tile_ctx_t * repair_tile_ctx,
286285
return buflen;
287286
}
288287

288+
static void
289+
fd_repair_handle_ping1( fd_repair_tile_ctx_t * repair_tile_ctx,
290+
fd_repair_t * glob,
291+
fd_stem_context_t * stem,
292+
fd_gossip_ping_t const * ping,
293+
uint const src_ip,
294+
uint const dst_port,
295+
ushort const src_port ) {
296+
uchar buf[1024];
297+
ulong buflen = fd_repair_handle_ping( repair_tile_ctx, glob, ping, buf, sizeof(buf) );
298+
ulong tsorig = fd_frag_meta_ts_comp( fd_tickcount() );
299+
send_packet( repair_tile_ctx, stem, 1, src_ip, src_port, dst_port, buf, buflen, tsorig );
300+
}
301+
289302
/* Pass a raw client response packet into the protocol. addr is the address of the sender */
290-
static int
303+
static void
291304
fd_repair_recv_clnt_packet( fd_repair_tile_ctx_t * repair_tile_ctx,
292305
fd_stem_context_t * stem,
293306
fd_repair_t * glob,
@@ -297,35 +310,25 @@ fd_repair_recv_clnt_packet( fd_repair_tile_ctx_t * repair_tile_ctx,
297310
uint dst_ip4_addr ) {
298311
glob->metrics.recv_clnt_pkt++;
299312

300-
FD_SCRATCH_SCOPE_BEGIN {
301-
while( 1 ) {
302-
ulong decoded_sz;
303-
fd_repair_response_t * gmsg = fd_bincode_decode1_scratch(
304-
repair_response, msg, msglen, NULL, &decoded_sz );
305-
if( FD_UNLIKELY( !gmsg ) ) {
306-
/* Solana falls back to assuming we got a shred in this case
307-
https://github.com/solana-labs/solana/blob/master/core/src/repair/serve_repair.rs#L1198 */
308-
break;
309-
}
310-
if( FD_UNLIKELY( decoded_sz != msglen ) ) {
311-
break;
312-
}
313-
314-
switch( gmsg->discriminant ) {
315-
case fd_repair_response_enum_ping:
316-
{
317-
uchar buf[1024];
318-
ulong buflen = fd_repair_handle_ping( repair_tile_ctx, glob, &gmsg->inner.ping, src_addr, dst_ip4_addr, buf, sizeof(buf) );
319-
ulong tsorig = fd_frag_meta_ts_comp( fd_tickcount() );
320-
send_packet( repair_tile_ctx, stem, 1, src_addr->addr, src_addr->port, dst_ip4_addr, buf, buflen, tsorig );
321-
break;
322-
}
323-
}
324-
325-
return 0;
313+
if( FD_UNLIKELY( msglen<sizeof(uint) ) ) {
314+
glob->metrics.recv_pkt_corrupted_msg++;
315+
return;
316+
}
317+
uint msg_type = FD_LOAD( uint, msg );
318+
msg += sizeof(uint);
319+
msglen -= sizeof(uint);
320+
321+
switch( msg_type ) {
322+
case 0: /* ping */
323+
if( FD_UNLIKELY( msglen!=132 ) ) {
324+
glob->metrics.recv_pkt_corrupted_msg++;
325+
return;
326326
}
327-
} FD_SCRATCH_SCOPE_END;
328-
return 0;
327+
fd_repair_handle_ping1( repair_tile_ctx, glob, stem, fd_type_pun_const( msg ), src_addr->addr, dst_ip4_addr, src_addr->port );
328+
break;
329+
default:
330+
break;
331+
}
329332
}
330333

331334
static ulong
@@ -427,7 +430,10 @@ before_frag( fd_repair_tile_ctx_t * ctx,
427430
ulong sig ) {
428431
uint in_kind = ctx->in_kind[ in_idx ];
429432
if( FD_LIKELY ( in_kind==IN_KIND_NET ) ) return fd_disco_netmux_sig_proto( sig )!=DST_PROTO_REPAIR;
430-
if( FD_UNLIKELY( in_kind==IN_KIND_SHRED ) ) return fd_int_if( fd_forest_root_slot( ctx->forest )==ULONG_MAX, -1, 0 ); /* not ready to read frag */
433+
if( FD_UNLIKELY( in_kind==IN_KIND_SHRED ) ) {
434+
if( FD_UNLIKELY( sig==ULONG_MAX ) ) return 0; /* repair ping */
435+
return fd_int_if( fd_forest_root_slot( ctx->forest )==ULONG_MAX, -1, 0 ); /* not ready to read frag */
436+
}
431437
return 0;
432438
}
433439

@@ -646,6 +652,13 @@ after_frag( fd_repair_tile_ctx_t * ctx,
646652
return;
647653
}
648654

655+
if( FD_UNLIKELY( in_kind==IN_KIND_NET && sig==ULONG_MAX ) ) {
656+
fd_repair_ping_fwd_t const * fwd = fd_type_pun_const( ctx->buffer );
657+
fd_gossip_ping_t const * ping = fd_type_pun_const( fwd->ping );
658+
fd_repair_handle_ping1( ctx, ctx->repair, stem, ping, fwd->src_ip4, fwd->dst_ip4, fwd->src_port );
659+
return;
660+
}
661+
649662
fd_eth_hdr_t const * eth = (fd_eth_hdr_t const *)ctx->buffer;
650663
fd_ip4_hdr_t const * ip4 = (fd_ip4_hdr_t const *)( (ulong)eth + sizeof(fd_eth_hdr_t) );
651664
fd_udp_hdr_t const * udp = (fd_udp_hdr_t const *)( (ulong)ip4 + FD_IP4_GET_LEN( *ip4 ) );

src/flamenco/types/fd_fuzz_types.h

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3675,23 +3675,6 @@ void *fd_repair_protocol_generate( void *mem, void **alloc_mem, fd_rng_t * rng )
36753675
return mem;
36763676
}
36773677

3678-
void fd_repair_response_inner_generate( fd_repair_response_inner_t * self, void **alloc_mem, uint discriminant, fd_rng_t * rng ) {
3679-
switch (discriminant) {
3680-
case 0: {
3681-
fd_gossip_ping_generate( &self->ping, alloc_mem, rng );
3682-
break;
3683-
}
3684-
}
3685-
}
3686-
void *fd_repair_response_generate( void *mem, void **alloc_mem, fd_rng_t * rng ) {
3687-
fd_repair_response_t *self = (fd_repair_response_t *) mem;
3688-
*alloc_mem = (uchar *) *alloc_mem + sizeof(fd_repair_response_t);
3689-
fd_repair_response_new(mem);
3690-
self->discriminant = fd_rng_uint( rng ) % 1;
3691-
fd_repair_response_inner_generate( &self->inner, alloc_mem, self->discriminant, rng );
3692-
return mem;
3693-
}
3694-
36953678
void fd_instr_error_enum_inner_generate( fd_instr_error_enum_inner_t * self, void **alloc_mem, uint discriminant, fd_rng_t * rng ) {
36963679
switch (discriminant) {
36973680
case 25: {

0 commit comments

Comments
 (0)