Line data Source code
1 : /* fd_quic_trace_rx_tile.c does passive decryption of incoming QUIC
2 : packets.
3 :
4 : It mocks the setup procedure and run loop of a real fd_quic_tile. */
5 :
6 : #include "fd_quic_trace.h"
7 : #include "../../../waltz/quic/fd_quic_private.h"
8 : #include "../../../waltz/quic/templ/fd_quic_parse_util.h"
9 : #include "../../../waltz/quic/fd_quic_proto.c"
10 : #include "../../../util/net/fd_eth.h"
11 : #include "../../../util/net/fd_ip4.h"
12 : #include "../../../util/net/fd_udp.h"
13 :
14 : static int
15 : before_frag( void * _ctx FD_FN_UNUSED,
16 : ulong in_idx,
17 : ulong seq,
18 0 : ulong sig ) {
19 0 : (void)sig;
20 :
21 : /* Skip non-QUIC packets */
22 0 : ulong proto = fd_disco_netmux_sig_proto( sig );
23 0 : if( proto!=DST_PROTO_TPU_QUIC ) return 1;
24 :
25 : /* Delay receive until fd_quic_tile is caught up */
26 0 : ulong * tgt_fseq = fd_quic_trace_target_fseq[ in_idx ];
27 0 : for(;;) {
28 0 : ulong tgt_seq = fd_fseq_query( tgt_fseq );
29 0 : if( FD_LIKELY( fd_seq_ge( tgt_seq, seq ) ) ) break;
30 0 : FD_SPIN_PAUSE();
31 0 : }
32 :
33 0 : return 0;
34 0 : }
35 :
36 : static void
37 : during_frag( void * _ctx FD_FN_UNUSED,
38 : ulong in_idx,
39 : ulong seq,
40 : ulong sig,
41 : ulong chunk,
42 0 : ulong sz ) {
43 0 : (void)in_idx; (void)seq; (void)sig;
44 0 : fd_quic_ctx_t * ctx = &fd_quic_trace_ctx;
45 0 : fd_memcpy( ctx->buffer, (uchar const *)fd_chunk_to_laddr_const( ctx->in_mem, chunk ), sz );
46 0 : }
47 :
48 : static int
49 : bounds_check_conn( fd_quic_t * quic,
50 0 : fd_quic_conn_t * conn ) {
51 0 : long conn_off = (long)((ulong)conn-(ulong)quic);
52 0 : return conn_off >= (long)quic->layout.conns_off && conn_off < (long)quic->layout.conn_map_off;
53 0 : }
54 :
55 : static void
56 : fd_quic_trace_initial( fd_quic_trace_ctx_t * trace_ctx,
57 : uchar * data,
58 : ulong data_sz,
59 : uint ip4_saddr,
60 0 : ushort udp_sport ) {
61 0 : fd_quic_ctx_t * ctx = &fd_quic_trace_ctx;
62 0 : fd_quic_t * quic = ctx->quic;
63 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
64 0 : fd_quic_conn_map_t * conn_map = translate_ptr( state->conn_map );
65 :
66 0 : if( FD_UNLIKELY( data_sz < FD_QUIC_SHORTEST_PKT ) ) return;
67 :
68 0 : fd_quic_initial_t initial[1] = {0};
69 0 : ulong rc = fd_quic_decode_initial( initial, data, data_sz );
70 0 : if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) {
71 0 : FD_LOG_DEBUG(( "fd_quic_decode_initial failed" ));
72 0 : return;
73 0 : }
74 0 : ulong len = (ulong)( initial->pkt_num_pnoff + initial->len );
75 0 : if( FD_UNLIKELY( len > data_sz ) ) {
76 0 : FD_LOG_DEBUG(( "Bogus initial packet length" ));
77 0 : return;
78 0 : }
79 :
80 0 : fd_quic_crypto_keys_t _keys[1];
81 0 : fd_quic_crypto_keys_t const * keys = NULL;
82 0 : if( initial->dst_conn_id_len == FD_QUIC_CONN_ID_SZ ) {
83 0 : ulong dst_conn_id = fd_ulong_load_8( initial->dst_conn_id );
84 0 : if( dst_conn_id==0 ) return;
85 0 : fd_quic_conn_map_t * conn_entry = fd_quic_conn_map_query( conn_map, dst_conn_id, NULL );
86 0 : if( conn_entry ) {
87 0 : fd_quic_conn_t * conn = translate_ptr( conn_entry->conn );
88 0 : if( FD_LIKELY( bounds_check_conn( quic, conn ) ) ) {
89 0 : keys = translate_ptr( &conn->keys[0][0] );
90 0 : }
91 0 : }
92 0 : }
93 0 : if( !keys ) {
94 : /* Set secrets->initial_secret */
95 0 : fd_quic_crypto_secrets_t secrets[1];
96 0 : fd_quic_gen_initial_secrets(
97 0 : secrets,
98 0 : initial->dst_conn_id, initial->dst_conn_id_len,
99 0 : /* is_client */ 0 );
100 :
101 : /* Derive secrets->secret[0][0] */
102 0 : fd_tls_hkdf_expand_label(
103 0 : secrets->secret[0][0], FD_QUIC_SECRET_SZ,
104 0 : secrets->initial_secret,
105 0 : FD_QUIC_CRYPTO_LABEL_CLIENT_IN,
106 0 : FD_QUIC_CRYPTO_LABEL_CLIENT_IN_LEN,
107 0 : NULL, 0UL );
108 :
109 : /* Derive decryption key */
110 0 : fd_quic_gen_keys( _keys, secrets->secret[0][0] );
111 0 : keys = _keys;
112 0 : }
113 :
114 0 : ulong pktnum_off = initial->pkt_num_pnoff;
115 0 : int hdr_err = fd_quic_crypto_decrypt_hdr( data, data_sz, pktnum_off, keys );
116 0 : if( hdr_err!=FD_QUIC_SUCCESS ) return;
117 :
118 0 : ulong pktnum_sz = fd_quic_h0_pkt_num_len( data[0] )+1u;
119 0 : ulong pktnum_comp = fd_quic_pktnum_decode( data+9UL, pktnum_sz );
120 0 : ulong pktnum = pktnum_comp; /* don't bother decompressing since initial pktnum is usually low */
121 :
122 0 : int crypt_err = fd_quic_crypto_decrypt( data, data_sz, pktnum_off, pktnum, keys );
123 0 : if( crypt_err!=FD_QUIC_SUCCESS ) return;
124 :
125 0 : ulong hdr_sz = pktnum_off + pktnum_sz;
126 0 : ulong wrap_sz = hdr_sz + FD_QUIC_CRYPTO_TAG_SZ;
127 0 : if( FD_UNLIKELY( data_sz<wrap_sz ) ) return;
128 :
129 0 : uchar conn_id_truncated[16] = {0};
130 0 : fd_memcpy( conn_id_truncated, initial->dst_conn_id, initial->dst_conn_id_len );
131 0 : fd_quic_trace_frame_ctx_t frame_ctx = {
132 0 : .conn_id = fd_ulong_load_8( conn_id_truncated ),
133 0 : .pkt_num = pktnum,
134 0 : .src_ip = ip4_saddr,
135 0 : .src_port = udp_sport,
136 0 : .pkt_type = FD_QUIC_PKT_TYPE_INITIAL
137 0 : };
138 :
139 0 : if( trace_ctx->dump ) {
140 0 : fd_quic_pretty_print_quic_pkt( &state->quic_pretty_print,
141 0 : state->now,
142 0 : data,
143 0 : data_sz,
144 0 : "ingress",
145 0 : ip4_saddr,
146 0 : udp_sport );
147 0 : } else {
148 0 : fd_quic_trace_frames( &frame_ctx, data+hdr_sz, data_sz-wrap_sz );
149 0 : }
150 0 : }
151 :
152 : static void
153 : fd_quic_trace_handshake( fd_quic_trace_ctx_t * trace_ctx,
154 : uchar * data,
155 : ulong data_sz,
156 : uint ip4_saddr,
157 0 : ushort udp_sport ) {
158 0 : fd_quic_ctx_t * ctx = &fd_quic_trace_ctx;
159 0 : fd_quic_t * quic = ctx->quic;
160 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
161 0 : fd_quic_conn_map_t * conn_map = translate_ptr( state->conn_map );
162 :
163 0 : if( FD_UNLIKELY( data_sz < FD_QUIC_SHORTEST_PKT ) ) return;
164 :
165 0 : fd_quic_handshake_t handshake[1] = {0};
166 0 : ulong rc = fd_quic_decode_handshake( handshake, data, data_sz );
167 0 : if( FD_UNLIKELY( rc == FD_QUIC_PARSE_FAIL ) ) {
168 0 : FD_LOG_DEBUG(( "fd_quic_decode_handshake failed" ));
169 0 : return;
170 0 : }
171 0 : ulong len = (ulong)( handshake->pkt_num_pnoff + handshake->len );
172 0 : if( FD_UNLIKELY( len > data_sz ) ) {
173 0 : FD_LOG_DEBUG(( "Bogus handshake packet length" ));
174 0 : return;
175 0 : }
176 :
177 : /* keeping this logic similar to the equivalent in fd_quic_trace_initial */
178 : /* for future merging */
179 0 : fd_quic_crypto_keys_t const * keys = NULL;
180 0 : if( handshake->dst_conn_id_len == FD_QUIC_CONN_ID_SZ ) {
181 0 : ulong dst_conn_id = fd_ulong_load_8( handshake->dst_conn_id );
182 0 : if( dst_conn_id==0 ) return;
183 0 : fd_quic_conn_map_t * conn_entry = fd_quic_conn_map_query( conn_map, dst_conn_id, NULL );
184 0 : if( conn_entry ) {
185 0 : fd_quic_conn_t * conn = translate_ptr( conn_entry->conn );
186 0 : if( FD_LIKELY( bounds_check_conn( quic, conn ) ) ) {
187 0 : keys = translate_ptr( &conn->keys[0][0] );
188 0 : }
189 0 : }
190 0 : }
191 0 : if( !keys ) {
192 0 : return;
193 0 : }
194 :
195 0 : ulong pktnum_off = handshake->pkt_num_pnoff;
196 0 : int hdr_err = fd_quic_crypto_decrypt_hdr( data, data_sz, pktnum_off, keys );
197 0 : if( hdr_err!=FD_QUIC_SUCCESS ) return;
198 :
199 0 : ulong pktnum_sz = fd_quic_h0_pkt_num_len( data[0] )+1u;
200 0 : ulong pktnum_comp = fd_quic_pktnum_decode( data+9UL, pktnum_sz );
201 0 : ulong pktnum = pktnum_comp; /* TODO decompress */
202 :
203 0 : int crypt_err = fd_quic_crypto_decrypt( data, data_sz, pktnum_off, pktnum, keys );
204 0 : if( crypt_err!=FD_QUIC_SUCCESS ) return;
205 :
206 0 : ulong hdr_sz = pktnum_off + pktnum_sz;
207 0 : ulong wrap_sz = hdr_sz + FD_QUIC_CRYPTO_TAG_SZ;
208 0 : if( FD_UNLIKELY( data_sz<wrap_sz ) ) return;
209 :
210 0 : uchar conn_id_truncated[16] = {0};
211 0 : fd_memcpy( conn_id_truncated, handshake->dst_conn_id, handshake->dst_conn_id_len );
212 0 : fd_quic_trace_frame_ctx_t frame_ctx = {
213 0 : .conn_id = fd_ulong_load_8( conn_id_truncated ),
214 0 : .pkt_num = pktnum,
215 0 : .src_ip = ip4_saddr,
216 0 : .src_port = udp_sport,
217 0 : .pkt_type = FD_QUIC_PKT_TYPE_INITIAL
218 0 : };
219 :
220 0 : if( trace_ctx->dump ) {
221 0 : fd_quic_pretty_print_quic_pkt( &state->quic_pretty_print,
222 0 : state->now,
223 0 : data,
224 0 : data_sz,
225 0 : "ingress",
226 0 : ip4_saddr,
227 0 : udp_sport );
228 0 : } else {
229 0 : fd_quic_trace_frames( &frame_ctx, data+hdr_sz, data_sz-wrap_sz );
230 0 : }
231 0 : }
232 :
233 : static void
234 : fd_quic_trace_1rtt( fd_quic_trace_ctx_t * trace_ctx,
235 : uchar * data,
236 : ulong data_sz,
237 : uint ip4_saddr,
238 0 : ushort udp_sport ) {
239 0 : fd_quic_ctx_t * ctx = &fd_quic_trace_ctx;
240 0 : fd_quic_t * quic = ctx->quic;
241 0 : fd_quic_state_t * state = fd_quic_get_state( quic );
242 0 : fd_quic_conn_map_t * conn_map = translate_ptr( state->conn_map );
243 :
244 0 : if( FD_UNLIKELY( data_sz < FD_QUIC_SHORTEST_PKT ) ) return;
245 :
246 : /* Look up conn */
247 0 : ulong dst_conn_id = fd_ulong_load_8( data+1 );
248 0 : fd_quic_conn_map_t * conn_entry = fd_quic_conn_map_query( conn_map, dst_conn_id, NULL );
249 0 : if( !conn_entry || !dst_conn_id ) return;
250 0 : fd_quic_conn_t * conn = translate_ptr( conn_entry->conn );
251 0 : if( FD_UNLIKELY( !bounds_check_conn( quic, conn ) ) ) return;
252 :
253 0 : fd_quic_crypto_keys_t * keys = &conn->keys[ fd_quic_enc_level_appdata_id ][ 0 ];
254 :
255 0 : ulong pktnum_off = 9UL;
256 0 : int hdr_err = fd_quic_crypto_decrypt_hdr( data, data_sz, pktnum_off, keys );
257 0 : if( hdr_err!=FD_QUIC_SUCCESS ) return;
258 :
259 0 : ulong pktnum_sz = fd_quic_h0_pkt_num_len( data[0] )+1u;
260 0 : ulong pktnum_comp = fd_quic_pktnum_decode( data+9UL, pktnum_sz );
261 0 : ulong pktnum = fd_quic_reconstruct_pkt_num( pktnum_comp, pktnum_sz, conn->exp_pkt_number[2] );
262 0 : int crypt_err = fd_quic_crypto_decrypt( data, data_sz, pktnum_off, pktnum, keys );
263 0 : if( crypt_err!=FD_QUIC_SUCCESS ) return;
264 :
265 0 : ulong hdr_sz = pktnum_off + pktnum_sz;
266 0 : ulong wrap_sz = hdr_sz + FD_QUIC_CRYPTO_TAG_SZ;
267 0 : if( FD_UNLIKELY( data_sz<wrap_sz ) ) return;
268 :
269 0 : fd_quic_trace_frame_ctx_t frame_ctx = {
270 0 : .conn_id = dst_conn_id,
271 0 : .pkt_num = pktnum,
272 0 : .src_ip = ip4_saddr,
273 0 : .src_port = udp_sport,
274 0 : .pkt_type = FD_QUIC_PKT_TYPE_ONE_RTT
275 0 : };
276 :
277 0 : if( trace_ctx->dump ) {
278 0 : fd_quic_pretty_print_quic_pkt( &state->quic_pretty_print,
279 0 : state->now,
280 0 : data,
281 0 : data_sz,
282 0 : "ingress",
283 0 : ip4_saddr,
284 0 : udp_sport );
285 0 : } else {
286 0 : fd_quic_trace_frames( &frame_ctx, data+hdr_sz, data_sz-wrap_sz );
287 0 : }
288 :
289 0 : (void)ip4_saddr; (void)conn;
290 0 : }
291 :
292 : static void
293 : fd_quic_trace_pkt( void * ctx,
294 : uchar * data,
295 : ulong data_sz,
296 : uint ip4_saddr,
297 0 : ushort udp_sport ) {
298 : /* FIXME: for now, only handle 1-RTT */
299 0 : int is_long = fd_quic_h0_hdr_form( data[0] );
300 0 : if( is_long ) {
301 0 : switch( fd_quic_h0_long_packet_type( data[0] ) ) {
302 0 : case FD_QUIC_PKT_TYPE_INITIAL:
303 0 : fd_quic_trace_initial( ctx, data, data_sz, ip4_saddr, udp_sport );
304 0 : break;
305 0 : case FD_QUIC_PKT_TYPE_HANDSHAKE:
306 0 : fd_quic_trace_handshake( ctx, data, data_sz, ip4_saddr, udp_sport );
307 0 : break;
308 0 : }
309 :
310 : /* FD_QUIC_PKT_TYPE_RETRY as the server, we shouldn't be receiving RETRY packets */
311 : /* FD_QUIC_PKT_TYPE_ZERO_RTT we don't support 0-RTT packets */
312 0 : } else {
313 0 : fd_quic_trace_1rtt( ctx, data, data_sz, ip4_saddr, udp_sport );
314 0 : }
315 0 : }
316 :
317 : static void
318 : after_frag( void * _ctx FD_FN_UNUSED,
319 : ulong in_idx,
320 : ulong seq,
321 : ulong sig,
322 : ulong sz,
323 : ulong tsorig,
324 0 : fd_stem_context_t * stem ) {
325 0 : (void)in_idx; (void)seq; (void)sig; (void)sz; (void)tsorig; (void)stem;
326 :
327 0 : fd_quic_ctx_t * ctx = &fd_quic_trace_ctx;
328 :
329 0 : if( sz < FD_QUIC_SHORTEST_PKT ) return;
330 0 : if( sz > sizeof(ctx->buffer) ) return;
331 :
332 0 : uchar * cur = ctx->buffer;
333 0 : uchar * end = cur+sz;
334 :
335 0 : fd_eth_hdr_t const * eth_hdr = fd_type_pun_const( cur );
336 0 : cur += sizeof(fd_eth_hdr_t);
337 0 : if( FD_UNLIKELY( cur>end ) ) return;
338 0 : if( FD_UNLIKELY( fd_ushort_bswap( eth_hdr->net_type )!=FD_ETH_HDR_TYPE_IP ) ) return;
339 :
340 0 : fd_ip4_hdr_t const * ip4_hdr = fd_type_pun_const( cur );
341 0 : if( FD_UNLIKELY( cur+sizeof(fd_ip4_hdr_t) > end ) ) return;
342 0 : cur += FD_IP4_GET_LEN( *ip4_hdr );
343 0 : if( FD_UNLIKELY( cur>end ) ) return;
344 0 : if( FD_UNLIKELY( ip4_hdr->protocol!=FD_IP4_HDR_PROTOCOL_UDP ) ) return;
345 :
346 0 : fd_udp_hdr_t const * udp_hdr = fd_type_pun_const( cur );
347 0 : if( FD_UNLIKELY( cur+sizeof(fd_udp_hdr_t) > end ) ) return;
348 0 : cur += sizeof(fd_udp_hdr_t);
349 0 : if( FD_UNLIKELY( cur>end ) ) return;
350 0 : (void)udp_hdr;
351 :
352 0 : uint ip4_saddr = fd_uint_load_4( ip4_hdr->saddr_c );
353 0 : ushort udp_sport = fd_ushort_bswap( udp_hdr->net_sport );
354 0 : fd_quic_trace_pkt( _ctx, cur, (ulong)( end-cur ), ip4_saddr, udp_sport );
355 0 : }
356 :
357 :
358 : #define STEM_BURST (1UL)
359 : #define STEM_CALLBACK_CONTEXT_TYPE fd_quic_trace_ctx_t
360 : #define STEM_CALLBACK_CONTEXT_ALIGN 1
361 0 : #define STEM_CALLBACK_BEFORE_FRAG before_frag
362 0 : #define STEM_CALLBACK_DURING_FRAG during_frag
363 0 : #define STEM_CALLBACK_AFTER_FRAG after_frag
364 : #include "../../../disco/stem/fd_stem.c"
365 :
366 : void
367 0 : fd_quic_trace_rx_tile( fd_frag_meta_t const * in_mcache, int dump ) {
368 0 : fd_frag_meta_t const * in_mcache_tbl[1] = { in_mcache };
369 :
370 0 : uchar fseq_mem[ FD_FSEQ_FOOTPRINT ] __attribute__((aligned(FD_FSEQ_ALIGN)));
371 0 : ulong * fseq = fd_fseq_join( fd_fseq_new( fseq_mem, 0UL ) );
372 0 : ulong * fseq_tbl[1] = { fseq };
373 :
374 0 : fd_rng_t rng[1];
375 0 : FD_TEST( fd_rng_join( fd_rng_new( rng, (uint)fd_tickcount(), 0UL ) ) );
376 :
377 0 : uchar scratch[ sizeof(fd_stem_tile_in_t)+128 ] __attribute__((aligned(FD_STEM_SCRATCH_ALIGN)));
378 :
379 0 : fd_quic_trace_ctx_t ctx[1] = {{ .dump = dump }};
380 :
381 0 : stem_run1( /* in_cnt */ 1UL,
382 0 : /* in_mcache */ in_mcache_tbl,
383 0 : /* in_fseq */ fseq_tbl,
384 0 : /* out_cnt */ 0UL,
385 : /* out_mcache */ NULL,
386 0 : /* cons_cnt */ 0UL,
387 : /* cons_out */ NULL,
388 : /* cons_fseq */ NULL,
389 0 : /* stem_burst */ 1UL,
390 0 : /* stem_lazy */ 0L,
391 0 : /* rng */ rng,
392 0 : /* scratch */ scratch,
393 0 : /* ctx */ ctx );
394 :
395 0 : fd_fseq_delete( fd_fseq_leave( fseq ) );
396 0 : }
|