From 0414e81c05e4dc5b9066441990c8777556b465de Mon Sep 17 00:00:00 2001 From: nbridge-jump <123656380+nbridge-jump@users.noreply.github.com> Date: Thu, 14 Nov 2024 22:58:58 +0000 Subject: [PATCH] quic - pretty print quic trace --- src/app/fdctl/fdctl.h | 1 + src/app/fddev/quic_trace/fd_quic_trace.h | 8 +- src/app/fddev/quic_trace/fd_quic_trace_main.c | 4 +- .../fddev/quic_trace/fd_quic_trace_rx_tile.c | 151 ++++- src/waltz/quic/fd_quic.c | 2 + src/waltz/quic/fd_quic_conn.c | 21 + src/waltz/quic/fd_quic_conn.h | 52 +- src/waltz/quic/fd_quic_pretty_print.c | 614 ++++++++++++++++++ src/waltz/quic/fd_quic_pretty_print.h | 18 + src/waltz/quic/fd_quic_private.h | 5 + src/waltz/quic/templ/fd_quic_pretty_print.h | 80 +++ .../quic/templ/fd_quic_pretty_print_decl.h | 7 + 12 files changed, 921 insertions(+), 42 deletions(-) create mode 100644 src/waltz/quic/fd_quic_pretty_print.c create mode 100644 src/waltz/quic/fd_quic_pretty_print.h create mode 100644 src/waltz/quic/templ/fd_quic_pretty_print.h create mode 100644 src/waltz/quic/templ/fd_quic_pretty_print_decl.h diff --git a/src/app/fdctl/fdctl.h b/src/app/fdctl/fdctl.h index fbe672a193..8c95e122bc 100644 --- a/src/app/fdctl/fdctl.h +++ b/src/app/fdctl/fdctl.h @@ -92,6 +92,7 @@ typedef union { struct { int event; + int dump; /* whether the user requested --dump */ } quic_trace; } args_t; diff --git a/src/app/fddev/quic_trace/fd_quic_trace.h b/src/app/fddev/quic_trace/fd_quic_trace.h index 74b91b45f1..d3a11ad338 100644 --- a/src/app/fddev/quic_trace/fd_quic_trace.h +++ b/src/app/fddev/quic_trace/fd_quic_trace.h @@ -18,6 +18,12 @@ extern void const * fd_quic_trace_log_base; extern ulong ** fd_quic_trace_target_fseq; +struct fd_quic_trace_ctx { + int dump; /* whether the user requested --dump */ +}; + +typedef struct fd_quic_trace_ctx fd_quic_trace_ctx_t; + struct fd_quic_trace_frame_ctx { ulong conn_id; uint src_ip; @@ -36,7 +42,7 @@ fd_quic_trace_frames( fd_quic_trace_frame_ctx_t * context, ulong data_sz ); void -fd_quic_trace_rx_tile( fd_frag_meta_t const * in_mcache ); +fd_quic_trace_rx_tile( fd_frag_meta_t const * in_mcache, int dump ); void fd_quic_trace_log_tile( fd_frag_meta_t const * in_mcache ); diff --git a/src/app/fddev/quic_trace/fd_quic_trace_main.c b/src/app/fddev/quic_trace/fd_quic_trace_main.c index 3af401392c..a5f3852572 100644 --- a/src/app/fddev/quic_trace/fd_quic_trace_main.c +++ b/src/app/fddev/quic_trace/fd_quic_trace_main.c @@ -42,6 +42,8 @@ quic_trace_cmd_args( int * pargc, } else { FD_LOG_ERR(( "Unsupported QUIC event type \"%s\"", event )); } + + args->quic_trace.dump = fd_env_strip_cmdline_contains( pargc, pargv, "--dump" ); } void @@ -130,7 +132,7 @@ quic_trace_cmd_fn( args_t * args, FD_LOG_NOTICE(( "quic-trace starting ..." )); switch( args->quic_trace.event ) { case EVENT_STREAM: - fd_quic_trace_rx_tile( net_quic->mcache ); + fd_quic_trace_rx_tile( net_quic->mcache, args->quic_trace.dump ); break; case EVENT_ERROR: fd_quic_trace_log_tile( log_rx->mcache ); 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 index a4542ac3ec..4822580586 100644 --- a/src/app/fddev/quic_trace/fd_quic_trace_rx_tile.c +++ b/src/app/fddev/quic_trace/fd_quic_trace_rx_tile.c @@ -49,15 +49,15 @@ static int bounds_check_conn( fd_quic_t * quic, fd_quic_conn_t * conn ) { long conn_off = (long)((ulong)conn-(ulong)quic); - return conn_off >= (long)quic->layout.conn_map_off && conn_off < (long)quic->layout.hs_pool_off; + return conn_off >= (long)quic->layout.conns_off && conn_off < (long)quic->layout.conn_map_off; } static void -fd_quic_trace_initial( void * _ctx FD_FN_UNUSED, - uchar * data, - ulong data_sz, - uint ip4_saddr, - ushort udp_sport ) { +fd_quic_trace_initial( fd_quic_trace_ctx_t * trace_ctx, + 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 ); @@ -135,15 +135,107 @@ fd_quic_trace_initial( void * _ctx FD_FN_UNUSED, .src_port = udp_sport, .pkt_type = FD_QUIC_PKT_TYPE_INITIAL }; - fd_quic_trace_frames( &frame_ctx, data+hdr_sz, data_sz-wrap_sz ); + + if( trace_ctx->dump ) { + fd_quic_pretty_print_quic_pkt( &state->quic_pretty_print, + state->now, + data, + data_sz, + "ingress", + ip4_saddr, + udp_sport ); + } else { + fd_quic_trace_frames( &frame_ctx, data+hdr_sz, data_sz-wrap_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_trace_handshake( fd_quic_trace_ctx_t * trace_ctx, + 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; + + fd_quic_handshake_t handshake[1] = {0}; + ulong rc = fd_quic_decode_handshake( handshake, data, data_sz ); + if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) { + FD_LOG_DEBUG(( "fd_quic_decode_handshake failed" )); + return; + } + ulong len = (ulong)( handshake->pkt_num_pnoff + handshake->len ); + if( FD_UNLIKELY( len > data_sz ) ) { + FD_LOG_DEBUG(( "Bogus handshake packet length" )); + return; + } + + /* keeping this logic similar to the equivalent in fd_quic_trace_initial */ + /* for future merging */ + fd_quic_crypto_keys_t const * keys = NULL; + if( handshake->dst_conn_id_len == FD_QUIC_CONN_ID_SZ ) { + ulong dst_conn_id = fd_ulong_load_8( handshake->dst_conn_id ); + if( dst_conn_id==0 ) return; + fd_quic_conn_map_t * conn_entry = fd_quic_conn_map_query( conn_map, dst_conn_id, NULL ); + if( conn_entry ) { + fd_quic_conn_t * conn = translate_ptr( conn_entry->conn ); + if( FD_LIKELY( bounds_check_conn( quic, conn ) ) ) { + keys = translate_ptr( &conn->keys[0][0] ); + } + } + } + if( !keys ) { + return; + } + + ulong pktnum_off = handshake->pkt_num_pnoff; + int hdr_err = fd_quic_crypto_decrypt_hdr( data, data_sz, pktnum_off, keys ); + if( hdr_err!=FD_QUIC_SUCCESS ) return; + + ulong pktnum_sz = fd_quic_h0_pkt_num_len( data[0] )+1u; + ulong pktnum_comp = fd_quic_pktnum_decode( data+9UL, pktnum_sz ); + ulong pktnum = pktnum_comp; /* TODO decompress */ + + int crypt_err = fd_quic_crypto_decrypt( data, data_sz, pktnum_off, pktnum, keys ); + if( crypt_err!=FD_QUIC_SUCCESS ) return; + + ulong hdr_sz = pktnum_off + pktnum_sz; + ulong wrap_sz = hdr_sz + FD_QUIC_CRYPTO_TAG_SZ; + if( FD_UNLIKELY( data_szdst_conn_id, handshake->dst_conn_id_len ); + fd_quic_trace_frame_ctx_t frame_ctx = { + .conn_id = fd_ulong_load_8( conn_id_truncated ), + .pkt_num = pktnum, + .src_ip = ip4_saddr, + .src_port = udp_sport, + .pkt_type = FD_QUIC_PKT_TYPE_INITIAL + }; + + if( trace_ctx->dump ) { + fd_quic_pretty_print_quic_pkt( &state->quic_pretty_print, + state->now, + data, + data_sz, + "ingress", + ip4_saddr, + udp_sport ); + } else { + fd_quic_trace_frames( &frame_ctx, data+hdr_sz, data_sz-wrap_sz ); + } +} + +static void +fd_quic_trace_1rtt( fd_quic_trace_ctx_t * trace_ctx, + 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 ); @@ -181,23 +273,42 @@ fd_quic_trace_1rtt( void * _ctx FD_FN_UNUSED, .src_port = udp_sport, .pkt_type = FD_QUIC_PKT_TYPE_ONE_RTT }; - fd_quic_trace_frames( &frame_ctx, data+hdr_sz, data_sz-wrap_sz ); + + if( trace_ctx->dump ) { + fd_quic_pretty_print_quic_pkt( &state->quic_pretty_print, + state->now, + data, + data_sz, + "ingress", + ip4_saddr, + udp_sport ); + } else { + fd_quic_trace_frames( &frame_ctx, data+hdr_sz, data_sz-wrap_sz ); + } + + (void)ip4_saddr; (void)conn; } static void -fd_quic_trace_pkt( fd_quic_ctx_t * ctx, +fd_quic_trace_pkt( void * ctx, uchar * data, ulong data_sz, uint ip4_saddr, ushort udp_sport ) { /* FIXME: for now, only handle 1-RTT */ int is_long = fd_quic_h0_hdr_form( data[0] ); - if( !is_long ) { + if( is_long ) { switch( fd_quic_h0_long_packet_type( data[0] ) ) { case FD_QUIC_PKT_TYPE_INITIAL: fd_quic_trace_initial( ctx, data, data_sz, ip4_saddr, udp_sport ); break; + case FD_QUIC_PKT_TYPE_HANDSHAKE: + fd_quic_trace_handshake( ctx, data, data_sz, ip4_saddr, udp_sport ); + break; } + + /* FD_QUIC_PKT_TYPE_RETRY as the server, we shouldn't be receiving RETRY packets */ + /* FD_QUIC_PKT_TYPE_ZERO_RTT we don't support 0-RTT packets */ } else { fd_quic_trace_1rtt( ctx, data, data_sz, ip4_saddr, udp_sport ); } @@ -240,12 +351,12 @@ after_frag( void * _ctx FD_FN_UNUSED, 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 ); + 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_TYPE fd_quic_trace_ctx_t #define STEM_CALLBACK_CONTEXT_ALIGN 1 #define STEM_CALLBACK_BEFORE_FRAG before_frag #define STEM_CALLBACK_DURING_FRAG during_frag @@ -253,7 +364,7 @@ after_frag( void * _ctx FD_FN_UNUSED, #include "../../../disco/stem/fd_stem.c" void -fd_quic_trace_rx_tile( fd_frag_meta_t const * in_mcache ) { +fd_quic_trace_rx_tile( fd_frag_meta_t const * in_mcache, int dump ) { fd_frag_meta_t const * in_mcache_tbl[1] = { in_mcache }; uchar fseq_mem[ FD_FSEQ_FOOTPRINT ] __attribute__((aligned(FD_FSEQ_ALIGN))); @@ -265,6 +376,8 @@ fd_quic_trace_rx_tile( fd_frag_meta_t const * in_mcache ) { uchar scratch[ sizeof(fd_stem_tile_in_t)+128 ] __attribute__((aligned(FD_STEM_SCRATCH_ALIGN))); + fd_quic_trace_ctx_t ctx[1] = {{ .dump = dump }}; + stem_run1( /* in_cnt */ 1UL, /* in_mcache */ in_mcache_tbl, /* in_fseq */ fseq_tbl, @@ -277,7 +390,7 @@ fd_quic_trace_rx_tile( fd_frag_meta_t const * in_mcache ) { /* stem_lazy */ 0L, /* rng */ rng, /* scratch */ scratch, - /* ctx */ NULL ); + /* ctx */ ctx ); fd_fseq_delete( fd_fseq_leave( fseq ) ); } diff --git a/src/waltz/quic/fd_quic.c b/src/waltz/quic/fd_quic.c index 3c45f209ff..6d0046a59b 100644 --- a/src/waltz/quic/fd_quic.c +++ b/src/waltz/quic/fd_quic.c @@ -15,6 +15,8 @@ #include "templ/fd_quic_frames_templ.h" #include "templ/fd_quic_undefs.h" +#include "fd_quic_pretty_print.c" + #include "crypto/fd_quic_crypto_suites.h" #include "templ/fd_quic_transport_params.h" #include "templ/fd_quic_parse_util.h" diff --git a/src/waltz/quic/fd_quic_conn.c b/src/waltz/quic/fd_quic_conn.c index 587afa5036..67815df68c 100644 --- a/src/waltz/quic/fd_quic_conn.c +++ b/src/waltz/quic/fd_quic_conn.c @@ -156,3 +156,24 @@ void * fd_quic_conn_get_context( fd_quic_conn_t * conn ) { return conn->context; } + +char const * +fd_quic_conn_reason_name( uint reason ) { + /* define mapping from reason code to name as a c-string */ + static char const * fd_quic_conn_reason_names[] = { +# define COMMA , +# define _(NAME,CODE,DESC) \ + [CODE] = #NAME + FD_QUIC_REASON_CODES(_,COMMA) +# undef _ +# undef COMMA + }; + +# define ELEMENTS ( sizeof(fd_quic_conn_reason_names) / sizeof(fd_quic_conn_reason_names[0]) ) + + if( FD_UNLIKELY( reason >= ELEMENTS ) ) return "N/A"; + + char const * name = fd_quic_conn_reason_names[reason]; + + return name ? name : "N/A"; +} diff --git a/src/waltz/quic/fd_quic_conn.h b/src/waltz/quic/fd_quic_conn.h index 1bad5f1d74..7e210a5f61 100644 --- a/src/waltz/quic/fd_quic_conn.h +++ b/src/waltz/quic/fd_quic_conn.h @@ -17,30 +17,40 @@ #define FD_QUIC_CONN_STATE_CLOSE_PENDING 6 /* connection is closing */ #define FD_QUIC_CONN_STATE_DEAD 7 /* connection about to be freed */ +#define FD_QUIC_REASON_CODES(X,SEP) \ + X(NO_ERROR , 0x00 , "No error" ) SEP \ + X(INTERNAL_ERROR , 0x01 , "Implementation error" ) SEP \ + X(CONNECTION_REFUSED , 0x02 , "Server refuses a connection" ) SEP \ + X(FLOW_CONTROL_ERROR , 0x03 , "Flow control error" ) SEP \ + X(STREAM_LIMIT_ERROR , 0x04 , "Too many streams opened" ) SEP \ + X(STREAM_STATE_ERROR , 0x05 , "Frame received in invalid stream state" ) SEP \ + X(FINAL_SIZE_ERROR , 0x06 , "Change to final size" ) SEP \ + X(FRAME_ENCODING_ERROR , 0x07 , "Frame encoding error" ) SEP \ + X(TRANSPORT_PARAMETER_ERROR , 0x08 , "Error in transport parameters" ) SEP \ + X(CONNECTION_ID_LIMIT_ERROR , 0x09 , "Too many connection IDs received" ) SEP \ + X(PROTOCOL_VIOLATION , 0x0a , "Generic protocol violation" ) SEP \ + X(INVALID_TOKEN , 0x0b , "Invalid Token received" ) SEP \ + X(APPLICATION_ERROR , 0x0c , "Application error" ) SEP \ + X(CRYPTO_BUFFER_EXCEEDED , 0x0d , "CRYPTO data buffer overflowed" ) SEP \ + X(KEY_UPDATE_ERROR , 0x0e , "Invalid packet protection update" ) SEP \ + X(AEAD_LIMIT_REACHED , 0x0f , "Excessive use of packet protection keys" ) SEP \ + X(NO_VIABLE_PATH , 0x10 , "No viable network path exists" ) SEP \ + X(CRYPTO_BASE , 0x100 , "0x0100-0x01ff CRYPTO_ERROR TLS alert code" ) SEP \ + X(HANDSHAKE_FAILURE , 0x128 , "Handshake failed" ) + enum { - FD_QUIC_CONN_REASON_NO_ERROR = 0x00, /* No error */ - FD_QUIC_CONN_REASON_INTERNAL_ERROR = 0x01, /* Implementation error */ - FD_QUIC_CONN_REASON_CONNECTION_REFUSED = 0x02, /* Server refuses a connection */ - FD_QUIC_CONN_REASON_FLOW_CONTROL_ERROR = 0x03, /* Flow control error */ - FD_QUIC_CONN_REASON_STREAM_LIMIT_ERROR = 0x04, /* Too many streams opened */ - FD_QUIC_CONN_REASON_STREAM_STATE_ERROR = 0x05, /* Frame received in invalid stream state */ - FD_QUIC_CONN_REASON_FINAL_SIZE_ERROR = 0x06, /* Change to final size */ - FD_QUIC_CONN_REASON_FRAME_ENCODING_ERROR = 0x07, /* Frame encoding error */ - FD_QUIC_CONN_REASON_TRANSPORT_PARAMETER_ERROR = 0x08, /* Error in transport parameters */ - FD_QUIC_CONN_REASON_CONNECTION_ID_LIMIT_ERROR = 0x09, /* Too many connection IDs received */ - FD_QUIC_CONN_REASON_PROTOCOL_VIOLATION = 0x0a, /* Generic protocol violation */ - FD_QUIC_CONN_REASON_INVALID_TOKEN = 0x0b, /* Invalid Token received */ - FD_QUIC_CONN_REASON_APPLICATION_ERROR = 0x0c, /* Application error */ - FD_QUIC_CONN_REASON_CRYPTO_BUFFER_EXCEEDED = 0x0d, /* CRYPTO data buffer overflowed */ - FD_QUIC_CONN_REASON_KEY_UPDATE_ERROR = 0x0e, /* Invalid packet protection update */ - FD_QUIC_CONN_REASON_AEAD_LIMIT_REACHED = 0x0f, /* Excessive use of packet protection keys */ - FD_QUIC_CONN_REASON_NO_VIABLE_PATH = 0x10, /* No viable network path exists */ - FD_QUIC_CONN_REASON_CRYPTO_BASE = 0x100, /* 0x0100-0x01ff CRYPTO_ERROR TLS alert code*/ - /* QUIC permits the use of a generic code in place of a specific error code [...] - such as handshake_failure (0x0128 in QUIC). */ - FD_QUIC_CONN_REASON_HANDSHAKE_FAILURE = 0x128 /* Handshake failed. */ +# define COMMA , +# define _(NAME,CODE,DESC) \ + FD_QUIC_CONN_REASON_##NAME = CODE + FD_QUIC_REASON_CODES(_,COMMA) +# undef _ +# undef COMMA }; +char const * +fd_quic_conn_reason_name( uint reason ); + + struct fd_quic_conn_stream_rx { ulong rx_hi_stream_id; /* highest RX stream ID sent by peer + 4 */ ulong rx_sup_stream_id; /* highest allowed RX stream ID + 4 */ diff --git a/src/waltz/quic/fd_quic_pretty_print.c b/src/waltz/quic/fd_quic_pretty_print.c new file mode 100644 index 0000000000..872efeb199 --- /dev/null +++ b/src/waltz/quic/fd_quic_pretty_print.c @@ -0,0 +1,614 @@ +#pragma GCC diagnostic ignored "-Wtype-limits" + +#include "templ/fd_quic_pretty_print.h" +#include "templ/fd_quic_templ.h" +#include "templ/fd_quic_frames_templ.h" +#include "templ/fd_quic_undefs.h" +#include "fd_quic_private.h" +#include "fd_quic_pretty_print.h" + +/* Generate frame pretty-print */ + +#define FD_TEMPL_DEF_STRUCT_BEGIN(NAME) \ + static ulong fd_quic_pretty_print_frame_##NAME( \ + fd_quic_##NAME##_t * frame, \ + char ** out_buf, \ + ulong * out_buf_sz, \ + uchar const * const buf, \ + ulong const buf_sz \ + ) { \ + 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; \ + \ + fd_quic_pretty_print_struct_##NAME( out_buf, out_buf_sz, frame ); \ + \ + return (ulong)(p0-buf); \ + } +#include "templ/fd_quic_dft.h" +#include "templ/fd_quic_frames_templ.h" +#include "templ/fd_quic_undefs.h" + +#include "templ/fd_quic_frame.h" + + +#define safe_snprintf( out, sz, ... ) \ + (__extension__({ \ + int rtn = snprintf( (out), (sz), __VA_ARGS__ ); \ + if( rtn < 0 ) rtn = 0; \ + if( rtn > (int)(sz) ) rtn = (int)(sz); \ + (ulong)rtn; })) + +ulong +fd_quic_pretty_print_frame( char ** out_buf, + ulong * out_buf_sz, + uchar const * buf, + ulong buf_sz ) { + if( FD_UNLIKELY( buf_sz<1UL ) ) return FD_QUIC_PARSE_FAIL; + + uchar const * cur_buf = buf; + uchar const * buf_end = buf + buf_sz; + + if( cur_buf[0] == 0x00 ) { /* handle padding separately */ + ulong padding_cnt = 0; + while( cur_buf < buf_end && *cur_buf == 0x00 ) { + padding_cnt++; + cur_buf++; + } + + ulong sz = safe_snprintf( *out_buf, *out_buf_sz, "\"frame_type\": \"0-padding\", \"count\": %lu, ", padding_cnt ); + *out_buf += sz; + *out_buf_sz -= sz; + + if( FD_UNLIKELY( cur_buf == buf_end ) ) return (ulong)( cur_buf - buf ); + } + + /* Frame ID is technically a varint but it's sufficient to look at the + first byte. */ + uint id = cur_buf[0]; + if( FD_UNLIKELY( id >= FD_QUIC_FRAME_TYPE_CNT ) ) { + ulong sz = safe_snprintf( *out_buf, *out_buf_sz, "\"frame_type\": \"%x-unknown\", ...", (uint)id ); + *out_buf += sz; + *out_buf_sz -= sz; + + return FD_QUIC_PARSE_FAIL; + } + + ulong consumed = 0UL; + + union { +#define FD_TEMPL_DEF_STRUCT_BEGIN(NAME) fd_quic_##NAME##_t NAME; +#include "templ/fd_quic_dft.h" +#include "templ/fd_quic_frames_templ.h" +#include "templ/fd_quic_undefs.h" + } data; + + memset( &data, 0, sizeof( data ) ); + + /* tail call to frame handler */ + switch( id ) { + +# define F(T,MID,NAME,...) \ + case T: { \ + ulong sz = safe_snprintf( *out_buf, *out_buf_sz, "\"frame_type\": \"%u\", ", (uint)id ); \ + *out_buf += sz; \ + *out_buf_sz -= sz; \ + consumed = fd_quic_pretty_print_frame_##NAME##_frame( &data.NAME##_frame, out_buf, out_buf_sz, cur_buf, (ulong)( buf_end - cur_buf ) ); \ + break; \ + } + FD_QUIC_FRAME_TYPES(F) +# undef F + + default: + /* we're unable to consume more bytes, since this is invalid */ + /* TODO put error key in json */ + return FD_QUIC_PARSE_FAIL; + } + + if( consumed == FD_QUIC_PARSE_FAIL ) return FD_QUIC_PARSE_FAIL; + + /* handle repeating blocks, etc. */ + cur_buf += consumed; + + switch( id ) { + case 0x02: + case 0x03: + { + ulong ack_range_count = data.ack_frame.ack_range_count; + + /* skip ack ranges */ + for( ulong j = 0UL; j < ack_range_count; ++j ) { + fd_quic_ack_range_frag_t ack_range[1]; + ulong rc = fd_quic_decode_ack_range_frag( ack_range, cur_buf, (ulong)( buf_end - cur_buf ) ); + if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) { + ulong out_sz = safe_snprintf( *out_buf, *out_buf_sz, "\"err\": \"parse-ack-ranges\"" ); + *out_buf += out_sz; + *out_buf_sz -= out_sz; + return FD_QUIC_PARSE_FAIL; + } + + fd_quic_pretty_print_struct_ack_range_frag( out_buf, out_buf_sz, ack_range ); + } + + if( data.ack_frame.type & 1U ) { + fd_quic_ecn_counts_frag_t ecn_counts[1] = {0}; + ulong rc = fd_quic_decode_ecn_counts_frag( ecn_counts, cur_buf, (ulong)( buf_end - cur_buf ) ); + if( rc == FD_QUIC_PARSE_FAIL ) { + ulong out_sz = safe_snprintf( *out_buf, *out_buf_sz, "\"err\": \"parse-ecn-counts\"" ); + *out_buf += out_sz; + *out_buf_sz -= out_sz; + return FD_QUIC_PARSE_FAIL; + } + + cur_buf += rc; + } + + break; + } + + case 0x06: + { + ulong out_sz = 0; + ulong remain = (ulong)( buf_end - cur_buf ); + ulong data_sz = data.crypto_frame.length; + if( data_sz > remain ) { + out_sz = safe_snprintf( *out_buf, *out_buf_sz, "\"err\": \"overflow\", " ); + *out_buf += out_sz; + *out_buf_sz -= out_sz; + + return FD_QUIC_PARSE_FAIL; + } + + out_sz = safe_snprintf( *out_buf, *out_buf_sz, "\"data\": [ " ); + *out_buf += out_sz; + *out_buf_sz -= out_sz; + + for( ulong j = 0; j < data_sz; ++j ) { + out_sz = safe_snprintf( *out_buf, *out_buf_sz, "0x%02x, ", cur_buf[j] ); + *out_buf += out_sz; + *out_buf_sz -= out_sz; + } + + out_sz = safe_snprintf( *out_buf, *out_buf_sz, "], " ); + *out_buf += out_sz; + *out_buf_sz -= out_sz; + + cur_buf += data_sz; + + break; + } + + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x0e: + case 0x0f: + { + ulong remain = (ulong)( buf_end - cur_buf ); + + /* stream */ + /* optional fields decoding */ + ulong offset = 0; + ulong stream_id = 0; + ulong data_sz = remain; + ulong fin = 0; + + switch( id ) { +# define _0(ELSE,...) ELSE +# define _1(ELSE,...) __VA_ARGS__ +# define _CASE(OFF,LEN,FIN,NAME) \ + case 8 + OFF(0,4) + LEN(0,2) + FIN(0,1): \ + OFF(,offset = data.NAME.offset;) \ + LEN(,data_sz = data.NAME.length;) \ + FIN(,fin = 1;) \ + stream_id = data.NAME.stream_id; \ + break; + _CASE(_0,_0,_0,stream_8_frame) + _CASE(_0,_0,_1,stream_8_frame) + _CASE(_0,_1,_0,stream_a_frame) + _CASE(_0,_1,_1,stream_a_frame) + _CASE(_1,_0,_0,stream_c_frame) + _CASE(_1,_0,_1,stream_c_frame) + _CASE(_1,_1,_0,stream_e_frame) + _CASE(_1,_1,_1,stream_e_frame) +# undef _CASE +# undef _1 +# undef _0 + } + + ulong stream_type = stream_id & 3UL; + + ulong out_sz = safe_snprintf( + *out_buf, + *out_buf_sz, + "\"stream_type\": %u, \"offset\": %lu, \"length\": %lu, \"fin\": %lu, ", + (uint)stream_type, offset, data_sz, fin ); + *out_buf += out_sz; + *out_buf_sz -= out_sz; + + if( data_sz > remain ) { + out_sz = safe_snprintf( *out_buf, *out_buf_sz, "\"err\": \"overflow\", " ); + *out_buf += out_sz; + *out_buf_sz -= out_sz; + + return FD_QUIC_PARSE_FAIL; + } + + out_sz = safe_snprintf( *out_buf, *out_buf_sz, "\"data\": [ " ); + *out_buf += out_sz; + *out_buf_sz -= out_sz; + + for( ulong j = 0; j < data_sz; ++j ) { + out_sz = safe_snprintf( *out_buf, *out_buf_sz, "0x%02x, ", cur_buf[j] ); + *out_buf += out_sz; + *out_buf_sz -= out_sz; + } + + out_sz = safe_snprintf( *out_buf, *out_buf_sz, "], " ); + *out_buf += out_sz; + *out_buf_sz -= out_sz; + + cur_buf += data_sz; + + break; + } + + case 0x1c: /* connection close */ + { + /* the conn_close_1_frame structure is different to conn_close_0_frame */ + ulong reason_phrase_length = data.conn_close_0_frame.reason_phrase_length; + ulong remain = (ulong)( buf_end - cur_buf ); + if( FD_UNLIKELY( reason_phrase_length > remain ) ) { + ulong out_sz = safe_snprintf( *out_buf, *out_buf_sz, "\"err\": \"overflow\", " ); + *out_buf += out_sz; + *out_buf_sz -= out_sz; + + return FD_QUIC_PARSE_FAIL; + } + + /* TODO pretty print the reason phrase */ + + cur_buf += reason_phrase_length; + + break; + } + + case 0x1d: /* connection close */ + { + /* the conn_close_1_frame structure is different to conn_close_0_frame */ + ulong reason_phrase_length = data.conn_close_1_frame.reason_phrase_length; + ulong remain = (ulong)( buf_end - cur_buf ); + if( FD_UNLIKELY( reason_phrase_length > remain ) ) { + ulong out_sz = safe_snprintf( *out_buf, *out_buf_sz, "\"err\": \"overflow\", " ); + *out_buf += out_sz; + *out_buf_sz -= out_sz; + + return FD_QUIC_PARSE_FAIL; + } + + /* TODO pretty print the reason phrase */ + + cur_buf += reason_phrase_length; + + break; + } + } + + return (ulong)( cur_buf - buf ); + +} + +ulong +fd_quic_pretty_print_frames( char ** out_buf, + ulong * out_buf_sz, + uchar const * buf, + ulong buf_sz ); + + +ulong +fd_quic_pretty_print_quic_hdr_initial( char ** out_buf, + ulong * out_buf_sz, + uchar const ** frame_ptr, + ulong * frame_sz, + uchar const * buf, + ulong buf_sz ) { + ulong sz = safe_snprintf( *out_buf, *out_buf_sz, "\"hdr_type\": \"initial\", " ); + *out_buf += sz; + *out_buf_sz -= sz; + + fd_quic_initial_t initial[1] = {0}; + ulong rc = fd_quic_decode_initial( initial, buf, buf_sz ); + if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) { + ulong sz = safe_snprintf( *out_buf, *out_buf_sz, "\"err\": \"hdr_parse_failed\", " ); + *out_buf += sz; + *out_buf_sz -= sz; + + FD_LOG_HEXDUMP_ERR(( "hdr_parse_failed", buf, fd_ulong_min( 16, buf_sz ) )); + + return FD_QUIC_PARSE_FAIL; + } + + ulong body_sz = initial->len; /* not a protected field */ + ulong pn_offset = initial->pkt_num_pnoff; + ulong pkt_number_sz = fd_quic_h0_pkt_num_len( buf[0] ) + 1u; + ulong payload_off = pn_offset + pkt_number_sz; + + /* now we have decrypted packet number */ + ulong pkt_number = fd_quic_pktnum_decode( buf+pn_offset, pkt_number_sz ); + + /* write pkt_number_sz into initial, for tracing */ + /* note this is the raw packet number, which may have been truncated */ + initial->pkt_num = pkt_number; + + *frame_ptr = buf + payload_off; + *frame_sz = body_sz - pkt_number_sz - FD_QUIC_CRYPTO_TAG_SZ; /* total size of all frames in packet */ + + fd_quic_pretty_print_struct_initial( out_buf, out_buf_sz, initial ); + + return payload_off; +} + + +ulong +fd_quic_pretty_print_quic_hdr_handshake( char ** out_buf, + ulong * out_buf_sz, + uchar const ** frame_ptr, + ulong * frame_sz, + uchar const * buf, + ulong buf_sz ) { + ulong sz = safe_snprintf( *out_buf, *out_buf_sz, "\"hdr_type\": \"handshake\", " ); + *out_buf += sz; + *out_buf_sz -= sz; + + fd_quic_handshake_t handshake[1] = {0}; + ulong rc = fd_quic_decode_handshake( handshake, buf, buf_sz ); + if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) { + ulong sz = safe_snprintf( *out_buf, *out_buf_sz, "\"err\": \"hdr_parse_failed\", " ); + *out_buf += sz; + *out_buf_sz -= sz; + + FD_LOG_HEXDUMP_ERR(( "hdr_parse_failed", buf, fd_ulong_min( 16, buf_sz ) )); + + return FD_QUIC_PARSE_FAIL; + } + + ulong body_sz = handshake->len; /* not a protected field */ + ulong pn_offset = handshake->pkt_num_pnoff; + ulong pkt_number_sz = fd_quic_h0_pkt_num_len( buf[0] ) + 1u; + ulong payload_off = pn_offset + pkt_number_sz; + + /* now we have decrypted packet number */ + ulong pkt_number = fd_quic_pktnum_decode( buf+pn_offset, pkt_number_sz ); + + /* write pkt_number_sz into handshake, for tracing */ + /* note this is the raw packet number, which may have been truncated */ + handshake->pkt_num = pkt_number; + + *frame_ptr = buf + payload_off; + *frame_sz = body_sz - pkt_number_sz - FD_QUIC_CRYPTO_TAG_SZ; /* total size of all frames in packet */ + + fd_quic_pretty_print_struct_handshake( out_buf, out_buf_sz, handshake ); + + return payload_off; +} + + +ulong +fd_quic_pretty_print_quic_hdr_one_rtt( char ** out_buf, + ulong * out_buf_sz, + uchar const ** frame_ptr, + ulong * frame_sz, + uchar const * buf, + ulong buf_sz ) { + ulong sz = safe_snprintf( *out_buf, *out_buf_sz, "\"hdr_type\": \"1-rtt\", " ); + *out_buf += sz; + *out_buf_sz -= sz; + + fd_quic_one_rtt_t one_rtt[1] = {0}; + + /* hidden field needed by decode function */ + one_rtt->dst_conn_id_len = 8; + + ulong rc = fd_quic_decode_one_rtt( one_rtt, buf, buf_sz ); + if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) { + ulong sz = safe_snprintf( *out_buf, *out_buf_sz, "\"err\": \"hdr_parse_failed\", " ); + *out_buf += sz; + *out_buf_sz -= sz; + + FD_LOG_HEXDUMP_ERR(( "hdr_parse_failed", buf, fd_ulong_min( 16, buf_sz ) )); + + return FD_QUIC_PARSE_FAIL; + } + + ulong pn_offset = one_rtt->pkt_num_pnoff; + ulong pkt_number_sz = fd_quic_h0_pkt_num_len( buf[0] ) + 1u; + ulong payload_off = pn_offset + pkt_number_sz; + ulong payload_sz = buf_sz - pn_offset - pkt_number_sz; /* includes auth tag */ + + /* now we have decrypted packet number */ + ulong pkt_number = fd_quic_pktnum_decode( buf+pn_offset, pkt_number_sz ); + + /* write pkt_number_sz into one_rtt, for tracing */ + /* note this is the raw packet number, which may have been truncated */ + one_rtt->pkt_num = pkt_number; + + *frame_ptr = buf + payload_off; + *frame_sz = payload_sz - FD_QUIC_CRYPTO_TAG_SZ; /* total size of all frames in packet */ + + fd_quic_pretty_print_struct_one_rtt( out_buf, out_buf_sz, one_rtt ); + + return payload_off; +} + + +ulong +fd_quic_pretty_print_quic_hdr( char ** out_buf, + ulong * out_buf_sz, + uchar const ** frame_ptr, + ulong * frame_sz, + uchar const * buf, + ulong buf_sz ) { + ulong sz; + + uint first = (uint)buf[0]; + uint is_long = first >> 7u; + + if( !is_long ) { + return fd_quic_pretty_print_quic_hdr_one_rtt( out_buf, out_buf_sz, frame_ptr, frame_sz, buf, buf_sz ); + } + + uint long_type = ( first >> 4u ) & 0x03u; + + switch( long_type ) { + case 0x00: /* initial */ + return fd_quic_pretty_print_quic_hdr_initial( out_buf, out_buf_sz, frame_ptr, frame_sz, buf, buf_sz ); + case 0x01: /* 0-rtt - unused */ + sz = safe_snprintf( *out_buf, *out_buf_sz, "\"err\": \"0-rtt\", " ); + *out_buf += sz; + *out_buf_sz -= sz; + return FD_QUIC_PARSE_FAIL; + case 0x02: /* handshake */ + return fd_quic_pretty_print_quic_hdr_handshake( out_buf, out_buf_sz, frame_ptr, frame_sz, buf, buf_sz ); + case 0x03: + sz = safe_snprintf( *out_buf, *out_buf_sz, "\"err\": \"not-implemented-retry\", " ); + *out_buf += sz; + *out_buf_sz -= sz; + return FD_QUIC_PARSE_FAIL; + } + + /* long type is uint & 0x03u - yet gcc thinks this is reachable */ + return FD_QUIC_PARSE_FAIL; +} + + +/* 16 bytes allows for: xxx.xxx.xxx.xxx\0 */ +#define IP4_TO_STR_BUF_SZ 16 +static +char const * +ip4_to_str( char buf[IP4_TO_STR_BUF_SZ], uint ip4_addr ) { + /* can't use sizeof(buf) here, as technically typeof(buf) is char* */ + ulong sz = safe_snprintf( buf, IP4_TO_STR_BUF_SZ, FD_IP4_ADDR_FMT, FD_IP4_ADDR_FMT_ARGS( ip4_addr ) ); + /* should not be possible */ + if( FD_UNLIKELY( sz >= IP4_TO_STR_BUF_SZ ) ) buf[IP4_TO_STR_BUF_SZ-1] = '\0'; + return buf; +} + + +ulong +fd_quic_pretty_print_quic_pkt( fd_quic_pretty_print_t * pretty_print, + ulong now, + uchar const * buf, + ulong buf_sz, + char const * flow, + uint ip4_saddr, + ushort udp_sport ) { + (void)pretty_print; + (void)now; + + char tmp_buf[16]; + + if( buf == NULL ) return FD_QUIC_PARSE_FAIL; + + static FD_TL char pretty_print_buf[16384]; + + memset( pretty_print_buf, 0, sizeof( pretty_print_buf ) ); + + char * out_buf = &pretty_print_buf[0]; + ulong out_buf_sz = sizeof( pretty_print_buf ) - 1UL; + + uchar const * frame_ptr = NULL; + ulong frame_sz = 0; + + ulong sz = safe_snprintf( out_buf, out_buf_sz, "{ \"type\": \"packet\", \"flow\": \"%s\", ", flow ); + out_buf += sz; + out_buf_sz -= sz; + + /* */ sz = safe_snprintf( out_buf, out_buf_sz, "\"src_ip_addr\": \"%s\", \"src_udp_port\": \"%u\", ", + ip4_to_str( tmp_buf, ip4_saddr ), fd_ushort_bswap( udp_sport ) ); + out_buf += sz; + out_buf_sz -= sz; + + ulong hdr_rc = fd_quic_pretty_print_quic_hdr( &out_buf, + &out_buf_sz, + &frame_ptr, + &frame_sz, + buf, + buf_sz ); + if( hdr_rc == FD_QUIC_PARSE_FAIL ) { + sz = safe_snprintf( out_buf, out_buf_sz, "\"err\": \"parse_fail\" } " ); + out_buf += sz; + out_buf_sz -= sz; + + return FD_QUIC_PARSE_FAIL; + } + + sz = safe_snprintf( out_buf, out_buf_sz, "\"frames\": [ " ); + out_buf += sz; + out_buf_sz -= sz; + + ulong rc = fd_quic_pretty_print_frames( &out_buf, + &out_buf_sz, + frame_ptr, + frame_sz ); + if( rc == FD_QUIC_PARSE_FAIL ) { + sz = safe_snprintf( out_buf, out_buf_sz, "], \"err\": \"parse_fail\" }, " ); + out_buf += sz; + out_buf_sz -= sz; + printf( "\nTRACE: %s\n", pretty_print_buf ); + fflush( stdout ); + return FD_QUIC_PARSE_FAIL; + } + + sz = safe_snprintf( out_buf, out_buf_sz, "] }, " ); + out_buf += sz; + out_buf_sz -= sz; + + for( ulong j = 0; j < (ulong)( out_buf - pretty_print_buf); ++j ) { + if( pretty_print_buf[j] == '\0' ) pretty_print_buf[j] = '*'; + } + + printf( "TRACE: [ %s ]\n", pretty_print_buf ); + + return rc; +} + +ulong +fd_quic_pretty_print_frames( char ** out_buf, + ulong * out_buf_sz, + uchar const * buf, + ulong buf_sz ) { + uchar const * orig_buf = buf; + ulong sz; + + while( buf_sz > 0 ) { + sz = safe_snprintf( *out_buf, *out_buf_sz, "{ \"type\": \"frame\", " ); + *out_buf += sz; + *out_buf_sz -= sz; + + ulong rc = fd_quic_pretty_print_frame( out_buf, out_buf_sz, buf, buf_sz ); + if( rc == FD_QUIC_PARSE_FAIL ) { + sz = safe_snprintf( *out_buf, *out_buf_sz, "\"err\": \"parse_fail\" }, " ); + *out_buf += sz; + *out_buf_sz -= sz; + break; + } + + sz = safe_snprintf( *out_buf, *out_buf_sz, " }, " ); + *out_buf += sz; + *out_buf_sz -= sz; + + if( rc >= buf_sz ) break; + + buf += rc; + buf_sz -= rc; + } + + return (ulong)( buf - orig_buf ); +} diff --git a/src/waltz/quic/fd_quic_pretty_print.h b/src/waltz/quic/fd_quic_pretty_print.h new file mode 100644 index 0000000000..237934097f --- /dev/null +++ b/src/waltz/quic/fd_quic_pretty_print.h @@ -0,0 +1,18 @@ +#ifndef HEADER_fd_src_waltz_quic_fd_quic_pretty_print_h +#define HEADER_fd_src_waltz_quic_fd_quic_pretty_print_h + +struct fd_quic_pretty_print { + int x; /* dummy - probably don't need fd_quic_pretty_print_t */ +}; +typedef struct fd_quic_pretty_print fd_quic_pretty_print_t; + +ulong +fd_quic_pretty_print_quic_pkt( fd_quic_pretty_print_t * pretty_print, + ulong now, + uchar const * buf, + ulong buf_sz, + char const * flow, + uint ip4_saddr, + ushort udp_sport ); + +#endif diff --git a/src/waltz/quic/fd_quic_private.h b/src/waltz/quic/fd_quic_private.h index ab6fa3a603..183af27a3d 100644 --- a/src/waltz/quic/fd_quic_private.h +++ b/src/waltz/quic/fd_quic_private.h @@ -9,6 +9,7 @@ #include "fd_quic_pkt_meta.h" #include "tls/fd_quic_tls.h" #include "fd_quic_stream_pool.h" +#include "fd_quic_pretty_print.h" #include "../../util/net/fd_ip4.h" #include "../../util/net/fd_udp.h" @@ -49,6 +50,7 @@ struct fd_quic_svc_queue { typedef struct fd_quic_svc_queue fd_quic_svc_queue_t; + /* fd_quic_state_t is the internal state of an fd_quic_t. Valid for lifetime of join. */ @@ -102,6 +104,9 @@ struct __attribute__((aligned(16UL))) fd_quic_state_private { /* last arp/routing tables update */ ulong ip_table_upd; + /* state for QUIC sampling */ + fd_quic_pretty_print_t quic_pretty_print; + /* secret for generating RETRY tokens */ uchar retry_secret[FD_QUIC_RETRY_SECRET_SZ]; uchar retry_iv [FD_QUIC_RETRY_IV_SZ]; diff --git a/src/waltz/quic/templ/fd_quic_pretty_print.h b/src/waltz/quic/templ/fd_quic_pretty_print.h new file mode 100644 index 0000000000..4aefffe6f6 --- /dev/null +++ b/src/waltz/quic/templ/fd_quic_pretty_print.h @@ -0,0 +1,80 @@ +/* tracing functionality */ + +#define FD_TEMPL_DEF_STRUCT_BEGIN(NAME) \ + static inline \ + void \ + fd_quic_pretty_print_struct_##NAME( char ** out_buf, \ + ulong * out_buf_sz, \ + fd_quic_##NAME##_t const * data ) { \ + (void)data; \ + char * p = *out_buf; (void)p; \ + char * q = *out_buf + *out_buf_sz; (void)q; + +#define pretty_print(...) \ + (__extension__({ \ + int rtn = snprintf( (p), (ulong)((q)-(p)), __VA_ARGS__ ); \ + if( rtn < 0 ) rtn = 0; \ + if( rtn > (int)((q)-(p)) ) rtn = (int)((q)-(p)); \ + p += rtn; \ + (ulong)rtn; })) + +#define FD_QUIC_FMT_uchar "u" +#define FD_QUIC_FMT_ushort "u" +#define FD_QUIC_FMT_uint "u" +#define FD_QUIC_FMT_ulong "lu" + +#define FD_QUIC_HEX_FMT_uchar "x" +#define FD_QUIC_HEX_FMT_ushort "x" +#define FD_QUIC_HEX_FMT_uint "x" +#define FD_QUIC_HEX_FMT_ulong "lx" + +#define FD_TEMPL_MBR_ELEM(NAME,TYPE) \ + pretty_print( "\"" #NAME "\":" "%" FD_QUIC_FMT_##TYPE ", ", data->NAME ); + +#define FD_TEMPL_MBR_ELEM_VARINT(NAME,TYPE) \ + pretty_print( "\"" #NAME "\": %" FD_QUIC_FMT_##TYPE ", ", data->NAME ); + +#define FD_TEMPL_MBR_ELEM_PKTNUM(NAME,TYPE) \ + pretty_print( "\"" #NAME "\": %" FD_QUIC_FMT_##TYPE ", ", data->NAME ); + + +#define FD_TEMPL_MBR_ELEM_VAR(NAME,BITS_MIN,BITS_MAX,LEN_NAME) \ + do { \ + pretty_print( "\"" #NAME "\": [ " ); \ + ulong tmp_len = data->LEN_NAME; \ + if( tmp_len * 8 > BITS_MAX ) tmp_len = ( BITS_MAX + 7 ) / 8; \ + for( ulong j = 0; j < tmp_len; ++j ) { \ + pretty_print( "0x%2.2x, ", data->NAME[j] ); \ + } \ + pretty_print( " ], " ); \ + } while(0); + +#define FD_TEMPL_MBR_ELEM_VAR_RAW(NAME,BITS_MIN,BITS_MAX,LEN_NAME) \ + do { \ + pretty_print( "\"" #NAME "\": [ " ); \ + ulong tmp_len = data->LEN_NAME; \ + if( tmp_len * 8 > BITS_MAX ) tmp_len = ( BITS_MAX + 7 ) / 8; \ + for( ulong j = 0; j < tmp_len; ++j ) { \ + pretty_print( "0x%2.2x, ", data->NAME[j] ); \ + } \ + pretty_print( " ], " ); \ + } while(0); + +#define FD_TEMPL_MBR_ELEM_ARRAY(NAME,TYPE,BYTES_MIN,BYTES_MAX) \ + do { \ + pretty_print( "\"" #NAME "\": [ " ); \ + ulong tmp_len = data->NAME##_len; \ + if( tmp_len > BYTES_MAX ) tmp_len = BYTES_MAX; \ + for( ulong j = 0; j < tmp_len; ++j ) { \ + pretty_print( "0x%" FD_QUIC_HEX_FMT_##TYPE ", ", data->NAME[j] ); \ + } \ + pretty_print( " ], " ); \ + } while(0); + +#define FD_TEMPL_DEF_STRUCT_END(NAME) \ + *out_buf = p; \ + *out_buf_sz = (ulong)(q - p); \ + } + +#include "fd_quic_dft.h" + diff --git a/src/waltz/quic/templ/fd_quic_pretty_print_decl.h b/src/waltz/quic/templ/fd_quic_pretty_print_decl.h new file mode 100644 index 0000000000..acd429aeaa --- /dev/null +++ b/src/waltz/quic/templ/fd_quic_pretty_print_decl.h @@ -0,0 +1,7 @@ +#define FD_TEMPL_DEF_STRUCT_BEGIN(NAME) \ + static inline \ + void \ + fd_quic_pretty_print_struct_##NAME( fd_quic_##NAME##_t const * data ); + +#include "fd_quic_dft.h" +