|
| 1 | +#include "fd_backtest_shredcap.h" |
| 2 | +#include "../../ballet/shred/fd_shred.h" |
| 3 | +#include "../../util/net/fd_eth.h" |
| 4 | +#include "../../util/net/fd_ip4.h" |
| 5 | +#include "../../util/net/fd_udp.h" |
| 6 | +#include "../../util/net/fd_pcapng.h" |
| 7 | +#include "fd_shredcap.h" |
| 8 | +#include <stdio.h> |
| 9 | + |
| 10 | +struct fd_backtest_shredcap_private { |
| 11 | + FILE * file; |
| 12 | + void * iter_mem; |
| 13 | + fd_pcapng_iter_t * iter; |
| 14 | + ulong slot; |
| 15 | + uchar bank_hash[32]; |
| 16 | +}; |
| 17 | + |
| 18 | +FD_FN_CONST ulong |
| 19 | +fd_backtest_shredcap_align( void ) { |
| 20 | + return fd_ulong_max( alignof(fd_backtest_shredcap_t), fd_pcapng_iter_align() ); |
| 21 | +} |
| 22 | + |
| 23 | +FD_FN_CONST ulong |
| 24 | +fd_backtest_shredcap_footprint( void ) { |
| 25 | + ulong l = FD_LAYOUT_INIT; |
| 26 | + l = FD_LAYOUT_APPEND( l, alignof(fd_backtest_shredcap_t), sizeof(fd_backtest_shredcap_t) ); |
| 27 | + l = FD_LAYOUT_APPEND( l, fd_pcapng_iter_align(), fd_pcapng_iter_footprint() ); |
| 28 | + return FD_LAYOUT_FINI( l, fd_backtest_shredcap_align() ); |
| 29 | +} |
| 30 | + |
| 31 | +fd_backtest_shredcap_t * |
| 32 | +fd_backtest_shredcap_new( void * shmem, |
| 33 | + char const * path ) { |
| 34 | + FD_SCRATCH_ALLOC_INIT( l, shmem ); |
| 35 | + fd_backtest_shredcap_t * db = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_backtest_shredcap_t), sizeof(fd_backtest_shredcap_t) ); |
| 36 | + void * iter_mem = FD_SCRATCH_ALLOC_APPEND( l, fd_pcapng_iter_align(), fd_pcapng_iter_footprint() ); |
| 37 | + FD_SCRATCH_ALLOC_FINI( l, fd_backtest_shredcap_align() ); |
| 38 | + memset( db, 0, sizeof(fd_backtest_shredcap_t) ); |
| 39 | + |
| 40 | + FILE * file = fopen( path, "rb" ); |
| 41 | + if( FD_UNLIKELY( !file ) ) { |
| 42 | + FD_LOG_WARNING(( "fopen(%s,rb) failed", path )); |
| 43 | + return NULL; |
| 44 | + } |
| 45 | + |
| 46 | + db->file = file; |
| 47 | + db->iter_mem = iter_mem; |
| 48 | + db->iter = NULL; |
| 49 | + return db; |
| 50 | +} |
| 51 | + |
| 52 | +void * |
| 53 | +fd_backtest_shredcap_delete( fd_backtest_shredcap_t * db ) { |
| 54 | + FD_TEST( db->file ); |
| 55 | + if( FD_UNLIKELY( 0!=fclose( db->file ) ) ) FD_LOG_ERR(( "fclose failed" )); |
| 56 | + memset( db, 0, sizeof(fd_backtest_shredcap_t) ); |
| 57 | + return (void *)db; |
| 58 | +} |
| 59 | + |
| 60 | +#define SEEK_UNTIL( DB, COND ) \ |
| 61 | + __extension__({ \ |
| 62 | + fd_pcapng_iter_t * iter = (DB)->iter; \ |
| 63 | + fd_pcapng_frame_t * frame; \ |
| 64 | + for(;;) { \ |
| 65 | + frame = fd_pcapng_iter_next( iter ); \ |
| 66 | + if( !frame ) break; \ |
| 67 | + if( COND ) break; \ |
| 68 | + } \ |
| 69 | + frame; \ |
| 70 | + }) |
| 71 | + |
| 72 | +static fd_shredcap_bank_hash_v0_t * |
| 73 | +frame_peek_bank_hash( fd_pcapng_frame_t const * frame, |
| 74 | + fd_shredcap_bank_hash_v0_t * out ) { |
| 75 | + if( frame->type!=FD_PCAPNG_FRAME_ENHANCED ) return NULL; |
| 76 | + if( !frame->idb ) return NULL; |
| 77 | + fd_pcapng_idb_desc_t const * idb = frame->idb; |
| 78 | + if( idb->link_type!=FD_PCAPNG_LINKTYPE_USER0 ) return NULL; |
| 79 | + if( 0!=strcmp( idb->opts.name, "shredcap0" ) ) return NULL; |
| 80 | + if( frame->data_sz<sizeof(uint)+sizeof(fd_shredcap_bank_hash_v0_t) ) return NULL; |
| 81 | + uint type = FD_LOAD( uint, frame->data ); |
| 82 | + if( type!=FD_SHREDCAP_TYPE_BANK_HASH_V0 ) return NULL; |
| 83 | + memcpy( out, frame->data+sizeof(uint), sizeof(fd_shredcap_bank_hash_v0_t) ); |
| 84 | + return out; |
| 85 | +} |
| 86 | + |
| 87 | +static ulong |
| 88 | +frame_peek_root_slot( fd_pcapng_frame_t const * frame ) { |
| 89 | + fd_shredcap_bank_hash_v0_t bh; |
| 90 | + if( !frame_peek_bank_hash( frame, &bh ) ) return ULONG_MAX; |
| 91 | + return bh.slot; |
| 92 | +} |
| 93 | + |
| 94 | +void |
| 95 | +fd_backtest_shredcap_init( fd_backtest_shredcap_t * db, |
| 96 | + ulong root_slot ) { |
| 97 | + if( FD_UNLIKELY( fseek( db->file, 0L, SEEK_SET )!=0L ) ) { |
| 98 | + FD_LOG_ERR(( "fseek failed" )); |
| 99 | + } |
| 100 | + db->iter = fd_pcapng_iter_new( db->iter_mem, db->file ); |
| 101 | + |
| 102 | + fd_pcapng_iter_next( db->iter ); |
| 103 | + SEEK_UNTIL( db, __extension__({ |
| 104 | + ulong found = frame_peek_root_slot( frame ); |
| 105 | + found!=ULONG_MAX && found>=root_slot; |
| 106 | + })); |
| 107 | +} |
| 108 | + |
| 109 | +int |
| 110 | +fd_backtest_shredcap_next_root_slot( fd_backtest_shredcap_t * db, |
| 111 | + ulong * root_slot, |
| 112 | + ulong * shred_cnt ) { |
| 113 | + fd_pcapng_frame_t const * frame = SEEK_UNTIL( db, (frame_peek_root_slot( frame )!=ULONG_MAX) ); |
| 114 | + if( FD_UNLIKELY( !frame ) ) return 0; |
| 115 | + |
| 116 | + fd_shredcap_bank_hash_v0_t bh; |
| 117 | + if( FD_UNLIKELY( !frame_peek_bank_hash( frame, &bh ) ) ) { |
| 118 | + /* FIXME gracefully skip over unexpected control packets */ |
| 119 | + FD_LOG_ERR(( "expected bank_hash frame, found something else" )); |
| 120 | + } |
| 121 | + |
| 122 | + *root_slot = db->slot = bh.slot; |
| 123 | + *shred_cnt = bh.data_shred_cnt; |
| 124 | + memcpy( db->bank_hash, bh.bank_hash, 32 ); |
| 125 | + return 1; |
| 126 | +} |
| 127 | + |
| 128 | +static uchar const * |
| 129 | +find_ip4_hdr( fd_pcapng_frame_t const * frame, |
| 130 | + ulong * psz ) { |
| 131 | + *psz = 0UL; |
| 132 | + if( !frame->idb ) return NULL; |
| 133 | + |
| 134 | + FD_TEST( frame->type==FD_PCAPNG_FRAME_ENHANCED ); |
| 135 | + FD_TEST( frame->idb ); |
| 136 | + switch( frame->idb->link_type ) { |
| 137 | + case FD_PCAPNG_LINKTYPE_USER0: |
| 138 | + /* FIXME gracefully skip over unexpected control packets */ |
| 139 | + FD_LOG_ERR(( "expected shred, got control frag" )); |
| 140 | + case FD_PCAPNG_LINKTYPE_ETHERNET: { |
| 141 | + FD_TEST( frame->data_sz>=sizeof(fd_eth_hdr_t) ); |
| 142 | + fd_eth_hdr_t const * eth_hdr = (fd_eth_hdr_t const *)frame->data; |
| 143 | + FD_TEST( eth_hdr->net_type==fd_ushort_bswap( FD_ETH_HDR_TYPE_IP ) ); |
| 144 | + *psz = frame->data_sz - sizeof(fd_eth_hdr_t); |
| 145 | + return frame->data + sizeof(fd_eth_hdr_t); |
| 146 | + } |
| 147 | + case FD_PCAPNG_LINKTYPE_RAW: |
| 148 | + case FD_PCAPNG_LINKTYPE_IPV4: |
| 149 | + *psz = frame->data_sz; |
| 150 | + return frame->data; |
| 151 | + default: |
| 152 | + return NULL; |
| 153 | + } |
| 154 | +} |
| 155 | + |
| 156 | +static uchar const * |
| 157 | +find_udp_payload( fd_pcapng_frame_t const * frame, |
| 158 | + ulong * psz ) { |
| 159 | + *psz = 0UL; |
| 160 | + ulong ip4_sz; |
| 161 | + uchar const * raw = find_ip4_hdr( frame, &ip4_sz ); |
| 162 | + fd_ip4_hdr_t const * ip4 = (fd_ip4_hdr_t const *)raw; |
| 163 | + if( FD_UNLIKELY( !raw ) ) return NULL; |
| 164 | + if( FD_UNLIKELY( ip4_sz<sizeof(fd_ip4_hdr_t)+sizeof(fd_udp_hdr_t) ) ) return NULL; |
| 165 | + if( FD_UNLIKELY( FD_IP4_GET_VERSION( *ip4 )!=4 ) ) return NULL; |
| 166 | + ulong ip4_hdr_len = FD_IP4_GET_LEN( *ip4 ); |
| 167 | + if( FD_UNLIKELY( ip4_sz<ip4_hdr_len+sizeof(fd_udp_hdr_t) ) ) return NULL; |
| 168 | + if( FD_UNLIKELY( ip4->protocol!=FD_IP4_HDR_PROTOCOL_UDP ) ) return NULL; |
| 169 | + *psz = ip4_sz - ip4_hdr_len - sizeof(fd_udp_hdr_t); |
| 170 | + return raw + ip4_hdr_len + sizeof(fd_udp_hdr_t); |
| 171 | +} |
| 172 | + |
| 173 | +void const * |
| 174 | +fd_backtest_shredcap_shred( fd_backtest_shredcap_t * db, |
| 175 | + ulong slot, |
| 176 | + ulong shred_idx ) { |
| 177 | + fd_pcapng_frame_t const * frame = fd_pcapng_iter_next( db->iter ); |
| 178 | + if( FD_UNLIKELY( !frame ) ) return NULL; |
| 179 | + |
| 180 | + ulong shred_sz = 0UL; |
| 181 | + fd_shred_t const * shred = fd_type_pun_const( find_udp_payload( frame, &shred_sz ) ); |
| 182 | + ulong shred_type = fd_shred_type( shred->variant ); |
| 183 | + if( FD_UNLIKELY( !shred_sz || !fd_shred_is_data( shred_type ) ) ) FD_LOG_ERR(( "failed to read shred %lu:%lu", slot, shred_idx )); |
| 184 | + if( FD_UNLIKELY( fd_shred_sz( shred )<shred_sz ) ) FD_LOG_ERR(( "corrupt shred %lu:%lu", slot, shred_idx )); |
| 185 | + if( FD_UNLIKELY( shred->slot!=slot ) ) FD_LOG_ERR(( "expected shred slot %lu, got %lu", slot, shred->slot )); |
| 186 | + if( FD_UNLIKELY( shred->idx!=shred_idx ) ) FD_LOG_ERR(( "expected shred idx %lu, got %u", shred_idx, shred->idx )); |
| 187 | + return shred; |
| 188 | +} |
| 189 | + |
| 190 | +uchar const * |
| 191 | +fd_backtest_shredcap_bank_hash( fd_backtest_shredcap_t * db, |
| 192 | + ulong slot ) { |
| 193 | + FD_TEST( slot==db->slot ); |
| 194 | + return db->bank_hash; |
| 195 | +} |
0 commit comments