Skip to content

Commit 979f1f5

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 2b00989 commit 979f1f5

File tree

9 files changed

+148
-230
lines changed

9 files changed

+148
-230
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 & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#define _GNU_SOURCE
4747

4848
#include "../../disco/topo/fd_topo.h"
49+
#include "../../disco/shred/fd_shred_tile.h"
4950
#include "generated/fd_repair_tile_seccomp.h"
5051

5152
#include "../../flamenco/repair/fd_repair.h"
@@ -326,14 +327,12 @@ send_packet( fd_repair_tile_ctx_t * ctx,
326327
ctx->net_out_chunk = fd_dcache_compact_next( chunk, packet_sz, ctx->net_out_chunk0, ctx->net_out_wmark );
327328
}
328329

329-
ulong
330-
fd_repair_handle_ping( fd_repair_tile_ctx_t * repair_tile_ctx,
331-
fd_repair_t * glob,
332-
fd_gossip_ping_t const * ping,
333-
fd_gossip_peer_addr_t const * peer_addr FD_PARAM_UNUSED,
334-
uint self_ip4_addr FD_PARAM_UNUSED,
335-
uchar * msg_buf,
336-
ulong msg_buf_sz ) {
330+
static ulong
331+
fd_repair_handle_ping( fd_repair_tile_ctx_t * repair_tile_ctx,
332+
fd_repair_t * glob,
333+
fd_gossip_ping_t const * ping,
334+
uchar * msg_buf,
335+
ulong msg_buf_sz ) {
337336
fd_repair_protocol_t protocol;
338337
fd_repair_protocol_new_disc(&protocol, fd_repair_protocol_enum_pong);
339338
fd_gossip_ping_t * pong = &protocol.inner.pong;
@@ -359,8 +358,22 @@ fd_repair_handle_ping( fd_repair_tile_ctx_t * repair_tile_ctx,
359358
return buflen;
360359
}
361360

361+
static void
362+
fd_repair_handle_ping1( fd_repair_tile_ctx_t * repair_tile_ctx,
363+
fd_repair_t * glob,
364+
fd_stem_context_t * stem,
365+
fd_gossip_ping_t const * ping,
366+
uint const src_ip,
367+
uint const dst_port,
368+
ushort const src_port ) {
369+
uchar buf[1024];
370+
ulong buflen = fd_repair_handle_ping( repair_tile_ctx, glob, ping, buf, sizeof(buf) );
371+
ulong tsorig = fd_frag_meta_ts_comp( fd_tickcount() );
372+
send_packet( repair_tile_ctx, stem, 1, src_ip, src_port, dst_port, buf, buflen, tsorig );
373+
}
374+
362375
/* Pass a raw client response packet into the protocol. addr is the address of the sender */
363-
static int
376+
static void
364377
fd_repair_recv_clnt_packet( fd_repair_tile_ctx_t * repair_tile_ctx,
365378
fd_stem_context_t * stem,
366379
fd_repair_t * glob,
@@ -370,35 +383,25 @@ fd_repair_recv_clnt_packet( fd_repair_tile_ctx_t * repair_tile_ctx,
370383
uint dst_ip4_addr ) {
371384
glob->metrics.recv_clnt_pkt++;
372385

373-
FD_SCRATCH_SCOPE_BEGIN {
374-
while( 1 ) {
375-
ulong decoded_sz;
376-
fd_repair_response_t * gmsg = fd_bincode_decode1_scratch(
377-
repair_response, msg, msglen, NULL, &decoded_sz );
378-
if( FD_UNLIKELY( !gmsg ) ) {
379-
/* Solana falls back to assuming we got a shred in this case
380-
https://github.com/solana-labs/solana/blob/master/core/src/repair/serve_repair.rs#L1198 */
381-
break;
382-
}
383-
if( FD_UNLIKELY( decoded_sz != msglen ) ) {
384-
break;
385-
}
386-
387-
switch( gmsg->discriminant ) {
388-
case fd_repair_response_enum_ping:
389-
{
390-
uchar buf[FD_REPAIR_MAX_SIGN_BUF_SIZE];
391-
ulong buflen = fd_repair_handle_ping( repair_tile_ctx, glob, &gmsg->inner.ping, src_addr, dst_ip4_addr, buf, sizeof(buf) );
392-
ulong tsorig = fd_frag_meta_ts_comp( fd_tickcount() );
393-
send_packet( repair_tile_ctx, stem, 1, src_addr->addr, src_addr->port, dst_ip4_addr, buf, buflen, tsorig );
394-
break;
395-
}
396-
}
397-
398-
return 0;
386+
if( FD_UNLIKELY( msglen<sizeof(uint) ) ) {
387+
glob->metrics.recv_pkt_corrupted_msg++;
388+
return;
389+
}
390+
uint msg_type = FD_LOAD( uint, msg );
391+
msg += sizeof(uint);
392+
msglen -= sizeof(uint);
393+
394+
switch( msg_type ) {
395+
case 0: /* ping */
396+
if( FD_UNLIKELY( msglen!=132 ) ) {
397+
glob->metrics.recv_pkt_corrupted_msg++;
398+
return;
399399
}
400-
} FD_SCRATCH_SCOPE_END;
401-
return 0;
400+
fd_repair_handle_ping1( repair_tile_ctx, glob, stem, fd_type_pun_const( msg ), src_addr->addr, dst_ip4_addr, src_addr->port );
401+
break;
402+
default:
403+
break;
404+
}
402405
}
403406

404407
/* Signs and prepares a repair protocol message for sending, either
@@ -618,7 +621,10 @@ before_frag( fd_repair_tile_ctx_t * ctx,
618621
ulong sig ) {
619622
uint in_kind = ctx->in_kind[ in_idx ];
620623
if( FD_LIKELY ( in_kind==IN_KIND_NET ) ) return fd_disco_netmux_sig_proto( sig )!=DST_PROTO_REPAIR;
621-
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 */
624+
if( FD_UNLIKELY( in_kind==IN_KIND_SHRED ) ) {
625+
if( FD_UNLIKELY( sig==ULONG_MAX ) ) return 0; /* repair ping */
626+
return fd_int_if( fd_forest_root_slot( ctx->forest )==ULONG_MAX, -1, 0 ); /* not ready to read frag */
627+
}
622628
return 0;
623629
}
624630

@@ -707,33 +713,6 @@ after_frag_snap( fd_repair_tile_ctx_t * ctx,
707713
// else fd_reasm_publish( ctx->reasm, &ctx->root_block_id );
708714
}
709715

710-
static ulong FD_FN_UNUSED
711-
fd_repair_send_ping( fd_repair_tile_ctx_t * repair_tile_ctx,
712-
fd_repair_t * glob,
713-
fd_pinged_elem_t * val,
714-
uchar * buf,
715-
ulong buflen ) {
716-
fd_repair_response_t gmsg;
717-
fd_repair_response_new_disc( &gmsg, fd_repair_response_enum_ping );
718-
fd_gossip_ping_t * ping = &gmsg.inner.ping;
719-
ping->from = *glob->public_key;
720-
721-
uchar pre_image[FD_PING_PRE_IMAGE_SZ];
722-
memcpy( pre_image, "SOLANA_PING_PONG", 16UL );
723-
memcpy( pre_image+16UL, val->token.uc, 32UL );
724-
725-
fd_sha256_hash( pre_image, FD_PING_PRE_IMAGE_SZ, &ping->token );
726-
727-
repair_signer( repair_tile_ctx, ping->signature.uc, pre_image, FD_PING_PRE_IMAGE_SZ, FD_KEYGUARD_SIGN_TYPE_SHA256_ED25519 );
728-
729-
fd_bincode_encode_ctx_t ctx;
730-
FD_TEST( buflen >= FD_REPAIR_MAX_SIGN_BUF_SIZE );
731-
ctx.data = buf;
732-
ctx.dataend = buf + buflen;
733-
FD_TEST(0 == fd_repair_response_encode(&gmsg, &ctx));
734-
return (ulong)((uchar*)ctx.data - buf);
735-
}
736-
737716
static void FD_FN_UNUSED
738717
fd_repair_recv_pong(fd_repair_t * glob, fd_gossip_ping_t const * pong, fd_gossip_peer_addr_t const * from) {
739718
fd_pinged_elem_t * val = fd_pinged_table_query(glob->pinged, from, NULL);
@@ -943,6 +922,13 @@ after_frag( fd_repair_tile_ctx_t * ctx,
943922
return;
944923
}
945924

925+
if( FD_UNLIKELY( in_kind==IN_KIND_NET && sig==ULONG_MAX ) ) {
926+
fd_repair_ping_fwd_t const * fwd = fd_type_pun_const( ctx->buffer );
927+
fd_gossip_ping_t const * ping = fd_type_pun_const( fwd->ping );
928+
fd_repair_handle_ping1( ctx, ctx->repair, stem, ping, fwd->src_ip4, fwd->dst_ip4, fwd->src_port );
929+
return;
930+
}
931+
946932
fd_eth_hdr_t const * eth = (fd_eth_hdr_t const *)ctx->buffer;
947933
fd_ip4_hdr_t const * ip4 = (fd_ip4_hdr_t const *)( (ulong)eth + sizeof(fd_eth_hdr_t) );
948934
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)