diff --git a/src/app/fdctl/fdctl.h b/src/app/fdctl/fdctl.h index d60a5f1409..626fe11182 100644 --- a/src/app/fdctl/fdctl.h +++ b/src/app/fdctl/fdctl.h @@ -96,6 +96,7 @@ typedef struct fd_caps_ctx fd_caps_ctx_t; typedef struct { const char * name; const char * description; + uchar is_diagnostic; /* 1 implies action should be allowed for prod debugging */ void (*args)( int * pargc, char *** pargv, args_t * args ); void (*perm)( args_t * args, fd_caps_ctx_t * caps, config_t * const config ); diff --git a/src/app/fdctl/run/tiles/fd_quic_tile.c b/src/app/fdctl/run/tiles/fd_quic_tile.c index 004214cf6a..526dc2f378 100644 --- a/src/app/fdctl/run/tiles/fd_quic_tile.c +++ b/src/app/fdctl/run/tiles/fd_quic_tile.c @@ -1,3 +1,4 @@ +#include "fd_quic_tile.h" #include "../../../../disco/metrics/fd_metrics.h" #include "../../../../disco/stem/fd_stem.h" #include "../../../../disco/topo/fd_topo.h" @@ -26,64 +27,6 @@ (multiplexer). An arbitrary number of QUIC tiles can be run. Each UDP flow must stick to one QUIC tile. */ -typedef struct { - fd_tpu_reasm_t * reasm; - - fd_stem_context_t * stem; - - fd_quic_t * quic; - fd_aio_t quic_tx_aio[1]; - -# define ED25519_PRIV_KEY_SZ (32) -# define ED25519_PUB_KEY_SZ (32) - uchar tls_priv_key[ ED25519_PRIV_KEY_SZ ]; - uchar tls_pub_key [ ED25519_PUB_KEY_SZ ]; - fd_sha512_t sha512[1]; /* used for signing */ - - uchar buffer[ FD_NET_MTU ]; - - ulong round_robin_cnt; - ulong round_robin_id; - - fd_wksp_t * in_mem; - ulong in_chunk0; - ulong in_wmark; - - fd_frag_meta_t * net_out_mcache; - ulong * net_out_sync; - ulong net_out_depth; - ulong net_out_seq; - - fd_wksp_t * net_out_mem; - ulong net_out_chunk0; - ulong net_out_wmark; - ulong net_out_chunk; - - fd_wksp_t * verify_out_mem; - - double ns_per_tick; - ulong last_tick; - ulong last_wall; - - struct { - ulong txns_received_udp; - ulong txns_received_quic_fast; - ulong txns_received_quic_frag; - ulong frag_ok_cnt; - ulong frag_gap_cnt; - ulong frag_dup_cnt; - long reasm_active; - ulong reasm_overrun; - ulong reasm_abandoned; - ulong reasm_started; - ulong udp_pkt_too_small; - ulong udp_pkt_too_large; - ulong quic_pkt_too_small; - ulong quic_txn_too_small; - ulong quic_txn_too_large; - } metrics; -} fd_quic_ctx_t; - FD_FN_CONST static inline fd_quic_limits_t quic_limits( fd_topo_tile_t const * tile ) { fd_quic_limits_t limits = { diff --git a/src/app/fdctl/run/tiles/fd_quic_tile.h b/src/app/fdctl/run/tiles/fd_quic_tile.h new file mode 100644 index 0000000000..4eef9d9b93 --- /dev/null +++ b/src/app/fdctl/run/tiles/fd_quic_tile.h @@ -0,0 +1,66 @@ +#ifndef HEADER_fd_src_app_fdctl_run_tiles_fd_quic_tile_h +#define HEADER_fd_src_app_fdctl_run_tiles_fd_quic_tile_h + +#include "../../../../disco/quic/fd_tpu.h" +#include "../../../../disco/stem/fd_stem.h" +#include "../../../../waltz/quic/fd_quic.h" + +typedef struct { + fd_tpu_reasm_t * reasm; + + fd_stem_context_t * stem; + + fd_quic_t * quic; + fd_aio_t quic_tx_aio[1]; + +# define ED25519_PRIV_KEY_SZ (32) +# define ED25519_PUB_KEY_SZ (32) + uchar tls_priv_key[ ED25519_PRIV_KEY_SZ ]; + uchar tls_pub_key [ ED25519_PUB_KEY_SZ ]; + fd_sha512_t sha512[1]; /* used for signing */ + + uchar buffer[ FD_NET_MTU ]; + + ulong round_robin_cnt; + ulong round_robin_id; + + fd_wksp_t * in_mem; + ulong in_chunk0; + ulong in_wmark; + + fd_frag_meta_t * net_out_mcache; + ulong * net_out_sync; + ulong net_out_depth; + ulong net_out_seq; + + fd_wksp_t * net_out_mem; + ulong net_out_chunk0; + ulong net_out_wmark; + ulong net_out_chunk; + + fd_wksp_t * verify_out_mem; + + double ns_per_tick; + ulong last_tick; + ulong last_wall; + + struct { + ulong txns_received_udp; + ulong txns_received_quic_fast; + ulong txns_received_quic_frag; + ulong frag_ok_cnt; + ulong frag_gap_cnt; + ulong frag_dup_cnt; + long reasm_active; + ulong reasm_overrun; + ulong reasm_abandoned; + ulong reasm_started; + ulong udp_pkt_too_small; + ulong udp_pkt_too_large; + ulong quic_pkt_too_small; + ulong quic_txn_too_small; + ulong quic_txn_too_large; + } metrics; +} fd_quic_ctx_t; + +#endif /* HEADER_fd_src_app_fdctl_run_tiles_fd_quic_tile_h */ diff --git a/src/app/fddev/fddev.h b/src/app/fddev/fddev.h index d8b9b04cfe..e4ede0ca60 100644 --- a/src/app/fddev/fddev.h +++ b/src/app/fddev/fddev.h @@ -122,4 +122,13 @@ void flame_cmd_fn( args_t * args, config_t * const config ); +void +quic_trace_cmd_args( int * pargc, + char *** pargv, + args_t * args); + +void +quic_trace_cmd_fn( args_t * args, + config_t * const config ); + #endif /* HEADER_fd_src_app_fddev_fddev_h */ diff --git a/src/app/fddev/main1.c b/src/app/fddev/main1.c index 6dfc309067..75dd870e3b 100644 --- a/src/app/fddev/main1.c +++ b/src/app/fddev/main1.c @@ -105,8 +105,9 @@ static action_t DEV_ACTIONS[] = { { .name = "txn", .args = txn_cmd_args, .fn = txn_cmd_fn, .perm = txn_cmd_perm }, { .name = "bench", .args = bench_cmd_args, .fn = bench_cmd_fn, .perm = bench_cmd_perm }, { .name = "load", .args = load_cmd_args, .fn = load_cmd_fn, .perm = load_cmd_perm }, - { .name = "dump", .args = dump_cmd_args, .fn = dump_cmd_fn, .perm = NULL }, - { .name = "flame", .args = flame_cmd_args, .fn = flame_cmd_fn, .perm = flame_cmd_perm }, + { .name = "dump", .args = dump_cmd_args, .fn = dump_cmd_fn, .perm = NULL, .is_diagnostic=1 }, + { .name = "flame", .args = flame_cmd_args, .fn = flame_cmd_fn, .perm = flame_cmd_perm, .is_diagnostic=1 }, + { .name = "quic-trace", .args = quic_trace_cmd_args, .fn = quic_trace_cmd_fn, .perm = NULL, .is_diagnostic=1 }, }; extern char fd_log_private_path[ 1024 ]; @@ -196,7 +197,7 @@ fddev_main( int argc, if( FD_UNLIKELY( !action ) ) FD_LOG_ERR(( "unknown subcommand `%s`", action_name )); - int is_allowed_live = !strcmp( action->name, "flame" ) || !strcmp( action->name, "dump" ); + int is_allowed_live = action->is_diagnostic==1; if( FD_UNLIKELY( config.is_live_cluster && !is_allowed_live ) ) FD_LOG_ERR(( "The `fddev` command is for development and test environments but your " "configuration targets a live cluster. Use `fdctl` if this is a " diff --git a/src/app/fddev/quic_trace/Local.mk b/src/app/fddev/quic_trace/Local.mk new file mode 100644 index 0000000000..09c3679f27 --- /dev/null +++ b/src/app/fddev/quic_trace/Local.mk @@ -0,0 +1,5 @@ +ifdef FD_HAS_SSE +$(call add-objs,fd_quic_trace_frame,fd_fddev) +$(call add-objs,fd_quic_trace_main,fd_fddev) +$(call add-objs,fd_quic_trace_rx_tile,fd_fddev) +endif diff --git a/src/app/fddev/quic_trace/fd_quic_trace.h b/src/app/fddev/quic_trace/fd_quic_trace.h new file mode 100644 index 0000000000..603bd38acf --- /dev/null +++ b/src/app/fddev/quic_trace/fd_quic_trace.h @@ -0,0 +1,51 @@ +#ifndef HEADER_fd_src_app_fddev_quic_trace_fd_quic_trace_h +#define HEADER_fd_src_app_fddev_quic_trace_fd_quic_trace_h + +#include "../../../disco/topo/fd_topo.h" +#include "../../fdctl/run/tiles/fd_quic_tile.h" + +/* fd_quic_trace_ctx is the relocated fd_quic_ctx_t of the target quic + tile. fd_quic_trace_ctx_remote is the original fd_quic_ctx_t, but + the pointer itself is in the local address space. */ + +extern fd_quic_ctx_t fd_quic_trace_ctx; +extern fd_quic_ctx_t const * fd_quic_trace_ctx_remote; +extern ulong fd_quic_trace_ctx_raddr; +extern ulong volatile * fd_quic_trace_link_metrics; + +/* fd_quic_trace_target_fseq are the fseq counters published by the + target quic tile */ + +extern ulong ** fd_quic_trace_target_fseq; + +/* fd_tile_quic_trace_rx is the tile in fd_quic_trace_tx_tile.c */ + +extern fd_topo_run_tile_t fd_tile_quic_trace_rx; + + +struct fd_quic_trace_frame_ctx { + ulong conn_id; + uint src_ip; + ushort src_port; + ulong pkt_num; +}; + +typedef struct fd_quic_trace_frame_ctx fd_quic_trace_frame_ctx_t; + +FD_PROTOTYPES_BEGIN + +void +fd_quic_trace_frames( fd_quic_trace_frame_ctx_t * context, + uchar const * data, + ulong data_sz ); + +FD_PROTOTYPES_END + + +#define translate_ptr( ptr ) __extension__({ \ + ulong rel = (ulong)(ptr) - fd_quic_trace_ctx_raddr; \ + ulong laddr = (ulong)fd_quic_trace_ctx_remote + rel; \ + (__typeof__(ptr))(laddr); \ + }) + +#endif /* HEADER_fd_src_app_fddev_quic_trace_fd_quic_trace_h */ diff --git a/src/app/fddev/quic_trace/fd_quic_trace_frame.c b/src/app/fddev/quic_trace/fd_quic_trace_frame.c new file mode 100644 index 0000000000..275a476de8 --- /dev/null +++ b/src/app/fddev/quic_trace/fd_quic_trace_frame.c @@ -0,0 +1,186 @@ +#include "fd_quic_trace.h" +#include "../../../waltz/quic/fd_quic_proto.c" +#include "../../../waltz/quic/templ/fd_quic_frame.h" + +#define FRAME_STUB(name) \ + static ulong \ + fd_quic_trace_##name##_frame( \ + void * context FD_PARAM_UNUSED, \ + fd_quic_##name##_frame_t * frame FD_PARAM_UNUSED, \ + uchar const * p FD_PARAM_UNUSED, \ + ulong p_sz FD_PARAM_UNUSED ) { \ + return 0UL; \ + } + +static ulong +fd_quic_trace_padding_frame( + void * context FD_PARAM_UNUSED, + fd_quic_padding_frame_t * frame FD_PARAM_UNUSED, + uchar const * p, + ulong p_sz ) { + ulong pad_sz; + for( pad_sz=0UL; pad_szack_range_count; j++ ) { + if( FD_UNLIKELY( p_end <= p ) ) return FD_QUIC_PARSE_FAIL; + + fd_quic_ack_range_frag_t ack_range[1]; + ulong rc = fd_quic_decode_ack_range_frag( ack_range, p, (ulong)( p_end - p ) ); + if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) return FD_QUIC_PARSE_FAIL; + p += rc; + } + + if( frame->type & 1U ) { + fd_quic_ecn_counts_frag_t ecn_counts[1]; + ulong rc = fd_quic_decode_ecn_counts_frag( ecn_counts, p, (ulong)( p_end - p ) ); + if( rc == FD_QUIC_PARSE_FAIL ) return FD_QUIC_PARSE_FAIL; + p += rc; + } + + return (ulong)( p - p_begin ); +} + +FRAME_STUB( reset_stream ) +FRAME_STUB( stop_sending ) + +static ulong +fd_quic_trace_crypto_frame( + void * context FD_PARAM_UNUSED, + fd_quic_crypto_frame_t * frame, + uchar const * p FD_PARAM_UNUSED, + ulong p_sz ) { + if( FD_UNLIKELY( frame->length > p_sz ) ) return FD_QUIC_PARSE_FAIL; + return frame->length; +} + +FRAME_STUB( new_token ) + +static ulong +fd_quic_trace_stream_frame( + fd_quic_trace_frame_ctx_t * context, + fd_quic_stream_frame_t * frame, + uchar const * p FD_PARAM_UNUSED, + ulong p_sz ) { + + ulong offset = fd_ulong_if( frame->offset_opt, frame->offset, 0UL ); + ulong length = fd_ulong_if( frame->length_opt, frame->length, p_sz ); + if( FD_UNLIKELY( length>p_sz ) ) return FD_QUIC_PARSE_FAIL; + + printf( "ts=%20ld conn_id=%016lx src_ip=%08x src_port=%5hu pktnum=%8lu sid=%8lu off=%4lu (%s) len=%4lu (%s) fin=%d\n", + fd_log_wallclock(), + context->conn_id, + fd_uint_bswap( context->src_ip ), + context->src_port, + context->pkt_num, + frame->stream_id, + offset, + frame->offset_opt ? "e" : "i", + length, + frame->length_opt ? "e" : "i", + frame->fin_opt ); + + return length; +} + +FRAME_STUB( max_data ) +FRAME_STUB( max_stream_data ) +FRAME_STUB( max_streams ) +FRAME_STUB( data_blocked ) +FRAME_STUB( stream_data_blocked ) +FRAME_STUB( streams_blocked ) +FRAME_STUB( new_conn_id ) +FRAME_STUB( retire_conn_id ) +FRAME_STUB( path_challenge ) +FRAME_STUB( path_response ) + +static ulong +fd_quic_trace_conn_close_0_frame( + void * context FD_PARAM_UNUSED, + fd_quic_conn_close_0_frame_t * frame, + uchar const * p FD_PARAM_UNUSED, + ulong p_sz ) { + if( FD_UNLIKELY( frame->reason_phrase_length > p_sz ) ) return FD_QUIC_PARSE_FAIL; + return frame->reason_phrase_length; +} + +static ulong +fd_quic_trace_conn_close_1_frame( + void * context FD_PARAM_UNUSED, + fd_quic_conn_close_1_frame_t * frame, + uchar const * p FD_PARAM_UNUSED, + ulong p_sz ) { + if( FD_UNLIKELY( frame->reason_phrase_length > p_sz ) ) return FD_QUIC_PARSE_FAIL; + return frame->reason_phrase_length; +} + +FRAME_STUB( handshake_done ) + +#define FD_TEMPL_DEF_STRUCT_BEGIN(NAME) \ + static ulong fd_quic_trace1_##NAME( \ + void * const ctx, \ + uchar const * const buf, \ + ulong const buf_sz \ + ) { \ + fd_quic_##NAME##_t frame[1] = {0}; \ + uchar const * p0 = buf; \ + uchar const * const p1 = buf+buf_sz; \ + ulong rc; \ + \ + rc = fd_quic_decode_##NAME( frame, p0, (ulong)(p1-p0) ); \ + if( FD_UNLIKELY( rc==FD_QUIC_PARSE_FAIL ) ) return FD_QUIC_PARSE_FAIL;\ + p0 += rc; \ + \ + rc = fd_quic_trace_##NAME( ctx, frame, p0, (ulong)(p1-p0) ); \ + if( FD_UNLIKELY( rc==FD_QUIC_PARSE_FAIL ) ) return FD_QUIC_PARSE_FAIL;\ + p0 += rc; \ + \ + return (ulong)(p0-buf); \ + } +#include "../../../waltz/quic/templ/fd_quic_dft.h" +#include "../../../waltz/quic/templ/fd_quic_frames_templ.h" +#include "../../../waltz/quic/templ/fd_quic_undefs.h" + +static ulong +fd_quic_trace_frame( fd_quic_trace_frame_ctx_t * context, + uchar const * data, + ulong data_sz ) { + if( FD_UNLIKELY( data_sz<1UL ) ) return FD_QUIC_PARSE_FAIL; + (void)context; + + /* Frame ID is technically a varint but it's sufficient to look at the + first byte. */ + uint id = data[0]; + switch( id ) { +# define F(T,MID,NAME,...) \ + case T: return fd_quic_trace1_##NAME##_frame( context, data, data_sz ); +FD_QUIC_FRAME_TYPES(F) +# undef F + default: return FD_QUIC_PARSE_FAIL; + } +} + +void +fd_quic_trace_frames( fd_quic_trace_frame_ctx_t * context, + uchar const * data, + ulong data_sz ) { + while( data_sz ) { + ulong ret = fd_quic_trace_frame( context, data, data_sz ); + if( ret==FD_QUIC_PARSE_FAIL ) return; + if( FD_UNLIKELY( ret>data_sz ) ) return; + data += ret; + data_sz -= ret; + } +} diff --git a/src/app/fddev/quic_trace/fd_quic_trace_main.c b/src/app/fddev/quic_trace/fd_quic_trace_main.c new file mode 100644 index 0000000000..3f7f63b1d4 --- /dev/null +++ b/src/app/fddev/quic_trace/fd_quic_trace_main.c @@ -0,0 +1,121 @@ +/* This directory provides the 'fddev quic-trace' subcommand. + + The goal of quic-trace is to tap QUIC traffic on a live system, which + requires encryption keys and other annoying connection state. + + quic-trace does this by tapping into the shared memory segments of an + fd_quic_tile running on the same host. It does so strictly read-only + to minimize impact to a production system. + + This file (fd_quic_trace_main.c) provides the glue code required to + join remote fd_quic_tile objects. + + fd_quic_trace_rx_tile.c provides a fd_tango consumer for incoming + QUIC packets. */ + +#include "fd_quic_trace.h" +#include "../fddev.h" +#include "../../fdctl/run/tiles/fd_quic_tile.h" +#include "../../../tango/fseq/fd_fseq.h" + +/* Define global variables */ + +fd_quic_ctx_t fd_quic_trace_ctx; +fd_quic_ctx_t const * fd_quic_trace_ctx_remote; +ulong fd_quic_trace_ctx_raddr; +ulong ** fd_quic_trace_target_fseq; +ulong volatile * fd_quic_trace_link_metrics; + +void +quic_trace_cmd_args( int * pargc, + char *** pargv, + args_t * args ) { + (void)pargc; (void)pargv; (void)args; +} + +void +quic_trace_cmd_fn( args_t * args, + config_t * const config ) { + (void)args; + + fd_topo_t * topo = &config->topo; + fd_topo_join_workspaces( topo, FD_SHMEM_JOIN_MODE_READ_ONLY ); + fd_topo_fill( topo ); + + fd_topo_tile_t * quic_tile = NULL; + for( ulong tile_idx=0UL; tile_idx < topo->tile_cnt; tile_idx++ ) { + if( 0==strcmp( topo->tiles[ tile_idx ].name, "quic" ) ) { + quic_tile = &topo->tiles[ tile_idx ]; + break; + } + } + if( !quic_tile ) FD_LOG_ERR(( "QUIC tile not found in topology" )); + + /* Ugly: fd_quic_ctx_t uses non-relocatable object addressing. + We need to rebase pointers. foreign_{...} refer to the original + objects in shared memory, local_{...} refer to translated copies. */ + + void * quic_tile_base = fd_topo_obj_laddr( topo, quic_tile->tile_obj_id ); + fd_quic_ctx_t const * foreign_quic_ctx = quic_tile_base; + fd_quic_ctx_t * quic_ctx = &fd_quic_trace_ctx; + *quic_ctx = *foreign_quic_ctx; + fd_quic_trace_ctx_remote = foreign_quic_ctx; + + ulong quic_raddr = (ulong)foreign_quic_ctx->quic; + ulong ctx_raddr = quic_raddr - fd_ulong_align_up( sizeof(fd_quic_ctx_t), fd_ulong_max( alignof(fd_quic_ctx_t), fd_quic_align() ) ); + fd_quic_trace_ctx_raddr = ctx_raddr; + + FD_LOG_INFO(( "fd_quic_tile state at %p in tile address space", (void *)ctx_raddr )); + FD_LOG_INFO(( "fd_quic_tile state at %p in local address space", quic_tile_base )); + + quic_ctx->reasm = (void *)( (ulong)quic_tile_base + (ulong)quic_ctx->reasm - ctx_raddr ); + quic_ctx->stem = (void *)( (ulong)quic_tile_base + (ulong)quic_ctx->stem - ctx_raddr ); + quic_ctx->quic = (void *)( (ulong)quic_tile_base + (ulong)quic_ctx->quic - ctx_raddr ); + + fd_topo_link_t * net_quic = &topo->links[ quic_tile->in_link_id[ 0 ] ]; + quic_ctx->in_mem = topo->workspaces[ topo->objs[ net_quic->dcache_obj_id ].wksp_id ].wksp; + FD_LOG_INFO(( "net->quic link at %p", (void *)quic_ctx->in_mem )); + + /* Join shared memory objects + Mostly nops but verifies object magic numbers to ensure that + derived pointers are correct. */ + + FD_LOG_INFO(( "Joining fd_quic" )); + fd_quic_t * quic = fd_quic_join( quic_ctx->quic ); + if( !quic ) FD_LOG_ERR(( "Failed to join fd_quic" )); + + /* Locate original fseq objects + These are monitored to ensure the trace RX tile doesn't skip ahead + of the quic tile. */ + fd_quic_trace_target_fseq = malloc( quic_tile->in_cnt * sizeof(ulong) ); + for( ulong i=0UL; iin_cnt; i++ ) { + fd_quic_trace_target_fseq[ i ] = quic_tile->in_link_fseq[ i ]; + } + + /* Redirect metadata writes to dummy buffers. + Without this hack, stem_run would attempt to write metadata updates + into the target topology which is read-only. */ + + /* ... redirect metric updates */ + ulong * metrics = aligned_alloc( FD_METRICS_ALIGN, FD_METRICS_FOOTPRINT( quic_tile->in_cnt, quic_tile->out_cnt ) ); + if( !metrics ) FD_LOG_ERR(( "out of memory" )); + fd_memset( metrics, 0, FD_METRICS_FOOTPRINT( quic_tile->in_cnt, quic_tile->out_cnt ) ); + fd_metrics_register( metrics ); + + /* ... redirect fseq updates */ + for( ulong i=0UL; iin_cnt; i++ ) { + if( !quic_tile->in_link_poll[ i ] ) continue; + void * fseq_mem = aligned_alloc( fd_fseq_align(), fd_fseq_footprint() ); + if( !fseq_mem ) FD_LOG_ERR(( "out of memory" )); + quic_tile->in_link_fseq[ i ] = fd_fseq_join( fd_fseq_new( fseq_mem, ULONG_MAX ) ); + } + + fd_quic_trace_link_metrics = fd_metrics_link_in( fd_metrics_base_tl, 0 ); + + /* Join net->quic link consumer */ + + FD_LOG_NOTICE(( "quic-trace starting ..." )); + fd_topo_run_tile_t * rx_tile = &fd_tile_quic_trace_rx; + rx_tile->privileged_init( topo, quic_tile ); + rx_tile->run( topo, quic_tile ); +} diff --git a/src/app/fddev/quic_trace/fd_quic_trace_rx_tile.c b/src/app/fddev/quic_trace/fd_quic_trace_rx_tile.c new file mode 100644 index 0000000000..d366b1eefc --- /dev/null +++ b/src/app/fddev/quic_trace/fd_quic_trace_rx_tile.c @@ -0,0 +1,165 @@ +/* fd_quic_trace_rx_tile.c does passive decryption of incoming QUIC + packets. + + It mocks the setup procedure and run loop of a real fd_quic_tile. */ + +#include "fd_quic_trace.h" +#include "../../../waltz/quic/fd_quic_private.h" +#include "../../../waltz/quic/templ/fd_quic_parse_util.h" +#include "../../../util/net/fd_eth.h" +#include "../../../util/net/fd_ip4.h" +#include "../../../util/net/fd_udp.h" + +static void +privileged_init( fd_topo_t * topo, + fd_topo_tile_t * tile ) { + fd_quic_state_t * state = fd_quic_get_state( fd_quic_trace_ctx.quic ); + FD_LOG_INFO(( "fd_quic_t conn_map raddr %p", (void *)state->conn_map )); + FD_LOG_INFO(( "fd_quic_t conn map laddr %p", (void *)translate_ptr( state->conn_map ) )); + (void)topo; (void)tile; +} + +static int +before_frag( void * _ctx FD_FN_UNUSED, + ulong in_idx, + ulong seq, + ulong sig ) { + (void)sig; + + /* Skip non-QUIC packets */ + ulong proto = fd_disco_netmux_sig_proto( sig ); + if( proto!=DST_PROTO_TPU_QUIC ) return 1; + + /* Delay receive until fd_quic_tile is caught up */ + ulong * tgt_fseq = fd_quic_trace_target_fseq[ in_idx ]; + for(;;) { + ulong tgt_seq = fd_fseq_query( tgt_fseq ); + if( FD_LIKELY( tgt_seq>=seq ) ) break; + FD_SPIN_PAUSE(); + } + + return 0; +} + +static void +during_frag( void * _ctx FD_FN_UNUSED, + ulong in_idx, + ulong seq, + ulong sig, + ulong chunk, + ulong sz ) { + (void)in_idx; (void)seq; (void)sig; + fd_quic_ctx_t * ctx = &fd_quic_trace_ctx; + fd_memcpy( ctx->buffer, (uchar *)fd_chunk_to_laddr( ctx->in_mem, chunk ), sz ); +} + +static void +fd_quic_trace_1rtt( void * _ctx FD_FN_UNUSED, + uchar * data, + ulong data_sz, + uint ip4_saddr, + ushort udp_sport ) { + fd_quic_ctx_t * ctx = &fd_quic_trace_ctx; + fd_quic_t * quic = ctx->quic; + fd_quic_state_t * state = fd_quic_get_state( quic ); + fd_quic_conn_map_t * conn_map = translate_ptr( state->conn_map ); + + if( FD_UNLIKELY( data_sz < FD_QUIC_SHORTEST_PKT ) ) return; + + /* Look up conn */ + ulong dst_conn_id = fd_ulong_load_8( data+1 ); + fd_quic_conn_map_t * conn_entry = fd_quic_conn_map_query( conn_map, dst_conn_id, NULL ); + if( !conn_entry ) return; + fd_quic_conn_t * conn = translate_ptr( conn_entry->conn ); + fd_quic_crypto_keys_t * keys = &conn->keys[ fd_quic_enc_level_appdata_id ][ 0 ]; + + ulong pkt_number_off = 9UL; + int hdr_err = fd_quic_crypto_decrypt_hdr( data, data_sz, pkt_number_off, keys ); + if( hdr_err!=FD_QUIC_SUCCESS ) return; + + ulong pkt_num_sz = fd_quic_h0_pkt_num_len( data[0] )+1u; + ulong pkt_number = fd_quic_pktnum_decode( data+9UL, pkt_num_sz ); + int crypt_err = fd_quic_crypto_decrypt( data, data_sz, pkt_number_off, pkt_number, keys ); + if( crypt_err!=FD_QUIC_SUCCESS ) return; + + ulong hdr_sz = pkt_number_off + pkt_num_sz; + ulong wrap_sz = hdr_sz + FD_QUIC_CRYPTO_TAG_SZ; + if( FD_UNLIKELY( data_sz sizeof(ctx->buffer) ) return; + + uchar * cur = ctx->buffer; + uchar * end = cur+sz; + + fd_eth_hdr_t const * eth_hdr = fd_type_pun_const( cur ); + cur += sizeof(fd_eth_hdr_t); + if( FD_UNLIKELY( cur>end ) ) return; + if( FD_UNLIKELY( fd_ushort_bswap( eth_hdr->net_type )!=FD_ETH_HDR_TYPE_IP ) ) return; + + fd_ip4_hdr_t const * ip4_hdr = fd_type_pun_const( cur ); + if( FD_UNLIKELY( cur+sizeof(fd_ip4_hdr_t) > end ) ) return; + cur += FD_IP4_GET_LEN( *ip4_hdr ); + if( FD_UNLIKELY( cur>end ) ) return; + if( FD_UNLIKELY( ip4_hdr->protocol!=FD_IP4_HDR_PROTOCOL_UDP ) ) return; + + fd_udp_hdr_t const * udp_hdr = fd_type_pun_const( cur ); + if( FD_UNLIKELY( cur+sizeof(fd_udp_hdr_t) > end ) ) return; + cur += sizeof(fd_udp_hdr_t); + if( FD_UNLIKELY( cur>end ) ) return; + (void)udp_hdr; + + uint ip4_saddr = fd_uint_load_4( ip4_hdr->saddr_c ); + ushort udp_sport = fd_ushort_bswap( udp_hdr->net_sport ); + fd_quic_trace_pkt( ctx, cur, (ulong)( end-cur ), ip4_saddr, udp_sport ); +} + + +#define STEM_BURST (1UL) +#define STEM_CALLBACK_CONTEXT_TYPE void +#define STEM_CALLBACK_CONTEXT_ALIGN 1 +#define STEM_CALLBACK_BEFORE_FRAG before_frag +#define STEM_CALLBACK_DURING_FRAG during_frag +#define STEM_CALLBACK_AFTER_FRAG after_frag +#include "../../../disco/stem/fd_stem.c" + +fd_topo_run_tile_t fd_tile_quic_trace_rx = { + .name = "quic-trace-rx", + .privileged_init = privileged_init, + .run = stem_run, +}; diff --git a/src/waltz/quic/templ/fd_quic_frame.c b/src/waltz/quic/templ/fd_quic_frame.c index d904f8932f..22072abce6 100644 --- a/src/waltz/quic/templ/fd_quic_frame.c +++ b/src/waltz/quic/templ/fd_quic_frame.c @@ -3,51 +3,10 @@ #endif #define HEADER_fd_src_waltz_quic_templ_fd_quic_frame_types_templ_h +#include "fd_quic_frame.h" #include "../fd_quic_enum.h" #include "../../../util/fd_util.h" -/* FD_QUIC_FRAME_TYPES describes the frame types and their attributes. - - Columns: - - ID: frame type as seen on the wire - - MID: frame ID used in fd_quic metrics (FIXME use ID instead when fd_metrics supports sparse enums) */ - -#define FD_QUIC_FRAME_TYPES(X) \ -/* ID MID Handler Definition Pkts Spec */ \ - X(0x00, 21, padding, "Section 19.1 ", I, H, 0, 1, N, P) \ - X(0x01, 20, ping, "Section 19.2 ", I, H, 0, 1, , ) \ - X(0x02, 1, ack, "Section 19.3 ", I, H, _, 1, N, C) \ - X(0x03, 1, ack, "Section 19.3 ", I, H, _, 1, N, C) \ - X(0x04, 2, reset_stream, "Section 19.4 ", _, _, 0, 1, , ) \ - X(0x05, 3, stop_sending, "Section 19.5 ", _, _, 0, 1, , ) \ - X(0x06, 4, crypto, "Section 19.6 ", I, H, _, 1, , ) \ - X(0x07, 5, new_token, "Section 19.7 ", _, _, _, 1, , ) \ - X(0x08, 6, stream, "Section 19.8 ", _, _, 0, 1, F, ) \ - X(0x09, 6, stream, "Section 19.8 ", _, _, 0, 1, F, ) \ - X(0x0a, 6, stream, "Section 19.8 ", _, _, 0, 1, F, ) \ - X(0x0b, 6, stream, "Section 19.8 ", _, _, 0, 1, F, ) \ - X(0x0c, 6, stream, "Section 19.8 ", _, _, 0, 1, F, ) \ - X(0x0d, 6, stream, "Section 19.8 ", _, _, 0, 1, F, ) \ - X(0x0e, 6, stream, "Section 19.8 ", _, _, 0, 1, F, ) \ - X(0x0f, 6, stream, "Section 19.8 ", _, _, 0, 1, F, ) \ - X(0x10, 7, max_data, "Section 19.9 ", _, _, 0, 1, , ) \ - X(0x11, 8, max_stream_data, "Section 19.10", _, _, 0, 1, , ) \ - X(0x12, 9, max_streams, "Section 19.11", _, _, 0, 1, , ) \ - X(0x13, 9, max_streams, "Section 19.11", _, _, 0, 1, , ) \ - X(0x14, 10, data_blocked, "Section 19.12", _, _, 0, 1, , ) \ - X(0x15, 11, stream_data_blocked, "Section 19.13", _, _, 0, 1, , ) \ - X(0x16, 12, streams_blocked, "Section 19.14", _, _, 0, 1, , ) \ - X(0x17, 12, streams_blocked, "Section 19.14", _, _, 0, 1, , ) \ - X(0x18, 13, new_conn_id, "Section 19.15", _, _, 0, 1, P, ) \ - X(0x19, 14, retire_conn_id, "Section 19.16", _, _, 0, 1, , ) \ - X(0x1a, 15, path_challenge, "Section 19.17", _, _, 0, 1, P, ) \ - X(0x1b, 16, path_response, "Section 19.18", _, _, _, 1, P, ) \ - X(0x1c, 17, conn_close_0, "Section 19.19", I, H, 0, 1, N, ) \ - X(0x1d, 18, conn_close_1, "Section 19.19", _, _, 0, 1, N, ) \ - X(0x1e, 19, handshake_done, "Section 19.20", _, _, _, 1, , ) - -#define FD_QUIC_FRAME_TYPE_CNT (0x1f) /* lookup tables should have this many entries */ - /* Lookup table for allowed frame types *******************************/ diff --git a/src/waltz/quic/templ/fd_quic_frame.h b/src/waltz/quic/templ/fd_quic_frame.h new file mode 100644 index 0000000000..abdc27c027 --- /dev/null +++ b/src/waltz/quic/templ/fd_quic_frame.h @@ -0,0 +1,46 @@ +#ifndef HEADER_fd_src_waltz_quic_templ_fd_quic_frame_h +#define HEADER_fd_src_waltz_quic_templ_fd_quic_frame_h + +/* FD_QUIC_FRAME_TYPES describes the frame types and their attributes. + + Columns: + - ID: frame type as seen on the wire + - MID: frame ID used in fd_quic metrics (FIXME use ID instead when fd_metrics supports sparse enums) */ + +#define FD_QUIC_FRAME_TYPES(X) \ +/* ID MID Handler Definition Pkts Spec */ \ + X(0x00, 21, padding, "Section 19.1 ", I, H, 0, 1, N, P) \ + X(0x01, 20, ping, "Section 19.2 ", I, H, 0, 1, , ) \ + X(0x02, 1, ack, "Section 19.3 ", I, H, _, 1, N, C) \ + X(0x03, 1, ack, "Section 19.3 ", I, H, _, 1, N, C) \ + X(0x04, 2, reset_stream, "Section 19.4 ", _, _, 0, 1, , ) \ + X(0x05, 3, stop_sending, "Section 19.5 ", _, _, 0, 1, , ) \ + X(0x06, 4, crypto, "Section 19.6 ", I, H, _, 1, , ) \ + X(0x07, 5, new_token, "Section 19.7 ", _, _, _, 1, , ) \ + X(0x08, 6, stream, "Section 19.8 ", _, _, 0, 1, F, ) \ + X(0x09, 6, stream, "Section 19.8 ", _, _, 0, 1, F, ) \ + X(0x0a, 6, stream, "Section 19.8 ", _, _, 0, 1, F, ) \ + X(0x0b, 6, stream, "Section 19.8 ", _, _, 0, 1, F, ) \ + X(0x0c, 6, stream, "Section 19.8 ", _, _, 0, 1, F, ) \ + X(0x0d, 6, stream, "Section 19.8 ", _, _, 0, 1, F, ) \ + X(0x0e, 6, stream, "Section 19.8 ", _, _, 0, 1, F, ) \ + X(0x0f, 6, stream, "Section 19.8 ", _, _, 0, 1, F, ) \ + X(0x10, 7, max_data, "Section 19.9 ", _, _, 0, 1, , ) \ + X(0x11, 8, max_stream_data, "Section 19.10", _, _, 0, 1, , ) \ + X(0x12, 9, max_streams, "Section 19.11", _, _, 0, 1, , ) \ + X(0x13, 9, max_streams, "Section 19.11", _, _, 0, 1, , ) \ + X(0x14, 10, data_blocked, "Section 19.12", _, _, 0, 1, , ) \ + X(0x15, 11, stream_data_blocked, "Section 19.13", _, _, 0, 1, , ) \ + X(0x16, 12, streams_blocked, "Section 19.14", _, _, 0, 1, , ) \ + X(0x17, 12, streams_blocked, "Section 19.14", _, _, 0, 1, , ) \ + X(0x18, 13, new_conn_id, "Section 19.15", _, _, 0, 1, P, ) \ + X(0x19, 14, retire_conn_id, "Section 19.16", _, _, 0, 1, , ) \ + X(0x1a, 15, path_challenge, "Section 19.17", _, _, 0, 1, P, ) \ + X(0x1b, 16, path_response, "Section 19.18", _, _, _, 1, P, ) \ + X(0x1c, 17, conn_close_0, "Section 19.19", I, H, 0, 1, N, ) \ + X(0x1d, 18, conn_close_1, "Section 19.19", _, _, 0, 1, N, ) \ + X(0x1e, 19, handshake_done, "Section 19.20", _, _, _, 1, , ) + +#define FD_QUIC_FRAME_TYPE_CNT (0x1f) /* lookup tables should have this many entries */ + +#endif /* HEADER_fd_src_waltz_quic_templ_fd_quic_frame_h */