Line data Source code
1 : #include "../../shared/fd_config.h"
2 : #include "../../shared/fd_action.h"
3 :
4 : #include "../../platform/fd_sys_util.h"
5 : #include "../../platform/fd_net_util.h"
6 : #include "../../shared/commands/ready.h"
7 : #include "../../../ballet/base64/fd_base64.h"
8 : #include "../../../waltz/quic/fd_quic.h"
9 : #include "../../../waltz/quic/tests/fd_quic_test_helpers.h"
10 : #include "../../../waltz/tls/test_tls_helper.h"
11 : #include "../../../util/net/fd_ip4.h"
12 :
13 : #include <errno.h>
14 : #include <sys/random.h>
15 : #include <linux/capability.h>
16 :
17 : FD_IMPORT_BINARY(sample_transaction, "src/waltz/quic/tests/quic_txn.bin");
18 :
19 : static int g_conn_hs_complete = 0;
20 : static int g_conn_final = 0;
21 : static ulong g_stream_notify = 0UL;
22 :
23 : #define MAX_TXN_COUNT 128
24 :
25 : void
26 : txn_cmd_perm( args_t * args FD_PARAM_UNUSED,
27 : fd_cap_chk_t * chk,
28 0 : config_t const * config ) {
29 0 : if( FD_UNLIKELY( config->development.netns.enabled ) )
30 0 : fd_cap_chk_cap( chk, "txn", CAP_SYS_ADMIN, "enter a network namespace by calling `setns(2)`" );
31 0 : }
32 :
33 : void
34 : txn_cmd_args( int * pargc,
35 : char *** pargv,
36 0 : args_t * args ) {
37 0 : args->txn.payload_base64 = fd_env_strip_cmdline_cstr( pargc, pargv, "--payload-base64-encoded", NULL, NULL );
38 0 : args->txn.count = fd_env_strip_cmdline_ulong( pargc, pargv, "--count", NULL, 1 );
39 0 : if( FD_UNLIKELY( !args->txn.count || args->txn.count > MAX_TXN_COUNT ) )
40 0 : FD_LOG_ERR(( "count must be between 1 and %d", MAX_TXN_COUNT ));
41 :
42 0 : args->txn.dst_ip = fd_env_strip_cmdline_cstr( pargc, pargv, "--dst-ip", NULL, 0 );
43 0 : args->txn.dst_port = fd_env_strip_cmdline_ushort( pargc, pargv, "--dst-port", NULL, 0 );
44 0 : }
45 :
46 : static ulong
47 0 : cb_now( void * context ) {
48 0 : (void)context;
49 0 : return (ulong)fd_log_wallclock();
50 0 : }
51 :
52 : static void
53 : cb_conn_hs_complete( fd_quic_conn_t * conn,
54 0 : void * quic_ctx ) {
55 0 : (void)conn;
56 0 : (void)quic_ctx;
57 0 : g_conn_hs_complete = 1;
58 0 : }
59 :
60 : static void
61 : cb_conn_final( fd_quic_conn_t * conn,
62 0 : void * quic_ctx ) {
63 0 : (void)conn;
64 0 : (void)quic_ctx;
65 0 : g_conn_final = 1;
66 0 : }
67 :
68 : static void
69 : cb_stream_notify( fd_quic_stream_t * stream,
70 : void * stream_ctx,
71 0 : int notify_type ) {
72 0 : (void)stream;
73 0 : (void)stream_ctx;
74 0 : (void)notify_type;
75 0 : g_stream_notify += 1;
76 0 : }
77 :
78 : static void
79 : send_quic_transactions( fd_quic_t * quic,
80 : fd_quic_udpsock_t * udpsock,
81 : ulong count,
82 : uint dst_ip,
83 : ushort dst_port,
84 0 : fd_aio_pkt_info_t * pkt ) {
85 0 : fd_quic_set_aio_net_tx( quic, udpsock->aio );
86 0 : FD_TEST( fd_quic_init( quic ) );
87 :
88 0 : quic->cb.now = cb_now;
89 0 : quic->cb.conn_final = cb_conn_final;
90 0 : quic->cb.conn_hs_complete = cb_conn_hs_complete;
91 0 : quic->cb.stream_notify = cb_stream_notify;
92 :
93 0 : fd_quic_conn_t * conn = fd_quic_connect( quic, dst_ip, dst_port, 0U, (ushort)udpsock->listen_port );
94 0 : while ( FD_LIKELY( !( g_conn_hs_complete || g_conn_final ) ) ) {
95 0 : fd_quic_service( quic );
96 0 : fd_quic_udpsock_service( udpsock );
97 0 : }
98 0 : FD_TEST( conn );
99 0 : if( FD_UNLIKELY( conn->state != FD_QUIC_CONN_STATE_ACTIVE ) )
100 0 : FD_LOG_ERR(( "unable to connect to QUIC endpoint at "FD_IP4_ADDR_FMT":%hu, is it running? state is %u", FD_IP4_ADDR_FMT_ARGS(dst_ip), dst_port, conn->state ));
101 :
102 0 : ulong sent = 0;
103 0 : while( sent < count && !g_conn_final ) {
104 0 : fd_quic_stream_t * stream = fd_quic_conn_new_stream( conn );
105 0 : if( FD_UNLIKELY( !stream ) ) {
106 0 : fd_quic_service( quic );
107 0 : fd_quic_udpsock_service( udpsock );
108 0 : continue;
109 0 : }
110 :
111 0 : fd_aio_pkt_info_t * chunk = pkt + sent;
112 0 : int res = fd_quic_stream_send( stream, chunk->buf, chunk->buf_sz, 1 );
113 0 : if( FD_UNLIKELY( res != FD_QUIC_SUCCESS ) ) FD_LOG_ERR(( "fd_quic_stream_send failed (%d)", res ));
114 0 : sent += 1UL;
115 :
116 0 : fd_quic_service( quic );
117 0 : fd_quic_udpsock_service( udpsock );
118 0 : }
119 :
120 0 : while( FD_LIKELY( g_stream_notify!=count && !g_conn_final ) ) {
121 0 : fd_quic_service( quic );
122 0 : fd_quic_udpsock_service( udpsock );
123 0 : }
124 :
125 : /* close and wait for connection to complete */
126 0 : if( !g_conn_final ) {
127 0 : fd_quic_conn_close( conn, 0 );
128 0 : while( !g_conn_final ) {
129 0 : fd_quic_service( quic );
130 0 : fd_quic_udpsock_service( udpsock );
131 0 : }
132 0 : }
133 :
134 0 : fd_quic_fini( quic );
135 0 : }
136 :
137 : void
138 : txn_cmd_fn( args_t * args,
139 0 : config_t * config ) {
140 0 : if( FD_UNLIKELY( config->development.netns.enabled ) ) {
141 0 : if( FD_UNLIKELY( -1==fd_net_util_netns_enter( config->development.netns.interface1, NULL ) ) )
142 0 : FD_LOG_ERR(( "failed to enter network namespace `%s` (%i-%s)", config->development.netns.interface1, errno, fd_io_strerror( errno ) ));
143 0 : }
144 :
145 : /* wait until validator is ready to receive txns before sending */
146 0 : ready_cmd_fn( args, config );
147 :
148 0 : fd_quic_limits_t quic_limits = {
149 0 : .conn_cnt = 1UL,
150 0 : .handshake_cnt = 1UL,
151 0 : .conn_id_cnt = 4UL,
152 0 : .stream_id_cnt = 64UL,
153 0 : .inflight_frame_cnt = 64UL,
154 0 : .tx_buf_sz = fd_ulong_pow2_up( FD_TXN_MTU ),
155 0 : .stream_pool_cnt = 16UL
156 0 : };
157 0 : ulong quic_footprint = fd_quic_footprint( &quic_limits );
158 0 : FD_TEST( quic_footprint );
159 :
160 0 : fd_wksp_t * wksp = fd_wksp_new_anonymous( FD_SHMEM_NORMAL_PAGE_SZ,
161 0 : 1UL << 10,
162 0 : fd_shmem_cpu_idx( 0 ),
163 0 : "wksp",
164 0 : 0UL );
165 0 : FD_TEST( wksp );
166 0 : void * mem = fd_wksp_alloc_laddr( wksp, fd_quic_align(), quic_footprint, 1UL );
167 0 : fd_quic_t * quic = fd_quic_join( fd_quic_new( mem, &quic_limits ) );
168 0 : FD_TEST( quic );
169 :
170 : /* Signer */
171 0 : fd_rng_t _rng[1]; fd_rng_t * rng = fd_rng_join( fd_rng_new( _rng, 0U, 0UL ) );
172 0 : fd_tls_test_sign_ctx_t * sign_ctx = fd_wksp_alloc_laddr( wksp, alignof(fd_tls_test_sign_ctx_t), sizeof(fd_tls_test_sign_ctx_t), 1UL );
173 0 : fd_tls_test_sign_ctx( sign_ctx, rng );
174 :
175 0 : fd_memcpy( quic->config.identity_public_key, sign_ctx->public_key, 32UL );
176 0 : quic->config.sign_ctx = sign_ctx;
177 0 : quic->config.sign = fd_tls_test_sign_sign;
178 :
179 0 : fd_quic_udpsock_t _udpsock;
180 0 : fd_quic_udpsock_t * udpsock = fd_quic_client_create_udpsock( &_udpsock, wksp, fd_quic_get_aio_net_rx( quic ), 0 );
181 0 : FD_TEST( udpsock == &_udpsock );
182 :
183 0 : fd_quic_config_t * client_cfg = &quic->config;
184 0 : client_cfg->role = FD_QUIC_ROLE_CLIENT;
185 0 : client_cfg->idle_timeout = 200UL * 1000UL * 1000UL; /* 5000 millis */
186 0 : client_cfg->initial_rx_max_stream_data = 0; /* doesn't receive */
187 :
188 0 : fd_aio_pkt_info_t pkt[ MAX_TXN_COUNT ];
189 :
190 0 : if( FD_LIKELY( !args->txn.payload_base64 ) ) {
191 0 : FD_LOG_INFO(( "Transaction payload not specified, using hardcoded sample payload" ));
192 0 : for( ulong i=0; i<args->txn.count; i++ ) {
193 0 : pkt[ i ].buf = (void * )sample_transaction;
194 0 : pkt[ i ].buf_sz = (ushort )sample_transaction_sz;
195 0 : }
196 0 : } else {
197 0 : ulong payload_b64_sz = strlen( args->txn.payload_base64 );
198 :
199 0 : static uchar buf[ 1UL << 15UL ];
200 0 : if( FD_UNLIKELY( FD_BASE64_DEC_SZ( payload_b64_sz ) > sizeof(buf) ) )
201 0 : FD_LOG_ERR(( "Input payload is too large (max %lu bytes)", sizeof(buf) ));
202 :
203 0 : long buf_sz = fd_base64_decode( buf, args->txn.payload_base64, payload_b64_sz );
204 0 : if( FD_UNLIKELY( buf_sz<0L ) ) FD_LOG_ERR(( "bad payload input `%s`", args->txn.payload_base64 ));
205 :
206 0 : for( ulong i=0; i<args->txn.count; i++ ) {
207 0 : pkt[ i ].buf = (void * )buf;
208 0 : pkt[ i ].buf_sz = (ushort )buf_sz;
209 0 : }
210 0 : }
211 :
212 0 : uint dst_ip = config->net.ip_addr;
213 0 : if( FD_UNLIKELY( args->txn.dst_ip ) )
214 0 : if( FD_UNLIKELY( !fd_cstr_to_ip4_addr( args->txn.dst_ip, &dst_ip ) ) ) FD_LOG_ERR(( "invalid --dst-ip" ));
215 :
216 0 : ushort dst_port = config->tiles.quic.quic_transaction_listen_port;
217 0 : if( FD_UNLIKELY( args->txn.dst_port ) ) dst_port = args->txn.dst_port;
218 :
219 0 : FD_LOG_NOTICE(( "sending %lu transactions to "FD_IP4_ADDR_FMT":%hu", args->txn.count, FD_IP4_ADDR_FMT_ARGS(dst_ip), dst_port ));
220 :
221 0 : send_quic_transactions( quic, udpsock, args->txn.count, dst_ip, dst_port, pkt );
222 0 : fd_sys_util_exit_group( 0 );
223 0 : }
224 :
225 : action_t fd_action_txn = {
226 : .name = "txn",
227 : .args = txn_cmd_args,
228 : .fn = txn_cmd_fn,
229 : .perm = txn_cmd_perm,
230 : .description = "Send a transaction to an fddev instance"
231 : };
|