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