Skip to content

Commit 31f5fde

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 d735c12 commit 31f5fde

File tree

10 files changed

+152
-229
lines changed

10 files changed

+152
-229
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: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,29 @@
22
#define HEADER_fd_src_disco_shred_fd_shred_tile_h
33

44
#include "../tiles.h"
5+
#include "../../discof/repair/fd_repair_tile.h"
56
#include "../../flamenco/types/fd_types_custom.h"
67

8+
struct fd_repair_ping_fwd {
9+
uint src_ip4;
10+
uint dst_ip4;
11+
ushort src_port;
12+
13+
/* FIXME: Just have a wire-format struct here for the ping frame.
14+
This is currently not possible due to use of fd_types, which is not
15+
guaranteed to have the same in-memory format as the wire format. */
16+
uchar ping[ FD_REPAIR_PING_SZ ];
17+
};
18+
19+
typedef struct fd_repair_ping_fwd fd_repair_ping_fwd_t;
20+
721
/* Forward declarations */
822
typedef struct fd_fec_resolver fd_fec_resolver_t;
923
typedef struct fd_keyswitch_private fd_keyswitch_t;
1024
typedef struct fd_keyguard_client fd_keyguard_client_t;
1125

12-
/* Shred tile context structure */
26+
/* Part of the shred tile context struct
27+
FIXME remove this and just use fd_shred_ctx_t everywhere */
1328
typedef struct {
1429
fd_shredder_t * shredder;
1530
fd_fec_resolver_t * resolver;
@@ -27,6 +42,6 @@ typedef struct {
2742
fd_keyswitch_t * keyswitch;
2843
fd_keyguard_client_t keyguard_client[1];
2944
/* ... rest of the structure members ... */
30-
} fd_shred_ctx_t;
45+
} fd_shred_ctx_hdr_t;
3146

3247
#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);
@@ -835,6 +814,13 @@ after_frag( fd_repair_tile_ctx_t * ctx,
835814
return;
836815
}
837816

817+
if( FD_UNLIKELY( in_kind==IN_KIND_SHRED && sig==ULONG_MAX ) ) {
818+
fd_repair_ping_fwd_t const * fwd = fd_type_pun_const( ctx->buffer );
819+
fd_gossip_ping_t const * ping = fd_type_pun_const( fwd->ping );
820+
fd_repair_handle_ping1( ctx, ctx->repair, stem, ping, fwd->src_ip4, fwd->dst_ip4, fwd->src_port );
821+
return;
822+
}
823+
838824
if( FD_UNLIKELY( in_kind==IN_KIND_SHRED ) ) {
839825
fd_shred_t * shred = (fd_shred_t *)fd_type_pun( ctx->buffer );
840826
if( FD_UNLIKELY( shred->slot <= fd_forest_root_slot( ctx->forest ) ) ) {

src/discof/repair/fd_repair_tile.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef HEADER_fd_src_discof_repair_fd_repair_tile_h
2+
#define HEADER_fd_src_discof_repair_fd_repair_tile_h
3+
4+
/* FD_REPAIR_PING_SZ is the UDP payload size of a 'ping'-related packet.
5+
These incoming packets are forwarded to the repair tile. */
6+
#define FD_REPAIR_PING_SZ (132UL)
7+
8+
#endif /* HEADER_fd_src_discof_repair_fd_repair_tile_h */

src/flamenco/types/fd_fuzz_types.h

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3654,23 +3654,6 @@ void *fd_repair_protocol_generate( void *mem, void **alloc_mem, fd_rng_t * rng )
36543654
return mem;
36553655
}
36563656

3657-
void fd_repair_response_inner_generate( fd_repair_response_inner_t * self, void **alloc_mem, uint discriminant, fd_rng_t * rng ) {
3658-
switch (discriminant) {
3659-
case 0: {
3660-
fd_gossip_ping_generate( &self->ping, alloc_mem, rng );
3661-
break;
3662-
}
3663-
}
3664-
}
3665-
void *fd_repair_response_generate( void *mem, void **alloc_mem, fd_rng_t * rng ) {
3666-
fd_repair_response_t *self = (fd_repair_response_t *) mem;
3667-
*alloc_mem = (uchar *) *alloc_mem + sizeof(fd_repair_response_t);
3668-
fd_repair_response_new(mem);
3669-
self->discriminant = fd_rng_uint( rng ) % 1;
3670-
fd_repair_response_inner_generate( &self->inner, alloc_mem, self->discriminant, rng );
3671-
return mem;
3672-
}
3673-
36743657
void fd_instr_error_enum_inner_generate( fd_instr_error_enum_inner_t * self, void **alloc_mem, uint discriminant, fd_rng_t * rng ) {
36753658
switch (discriminant) {
36763659
case 25: {

0 commit comments

Comments
 (0)