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