Line data Source code
1 : #define _GNU_SOURCE
2 : #include "fd_config.h"
3 :
4 : #include "fd_config_parse.h"
5 : #include "fd_net_util.h"
6 : #include "fd_sys_util.h"
7 : #include "../../ballet/toml/fd_toml.h"
8 : #include "../../flamenco/genesis/fd_genesis_cluster.h"
9 :
10 : #include <errno.h>
11 : #include <stdlib.h> /* strtoul */
12 : #include <sys/mman.h>
13 : #include <fcntl.h>
14 : #include <unistd.h>
15 : #include <sys/utsname.h>
16 : #include <sys/stat.h>
17 :
18 : extern uchar const fdctl_default_config[];
19 : extern ulong const fdctl_default_config_sz;
20 :
21 : extern uchar const fdctl_default_firedancer_config[];
22 : extern ulong const fdctl_default_firedancer_config_sz;
23 :
24 : /* FD_TOML_POD_SZ sets the buffer size of the fd_pod that will hold the
25 : parsed config file content.
26 :
27 : This should be large enough to hold a Firedancer TOML file with all
28 : config options set. */
29 :
30 : #define FD_TOML_POD_SZ (1UL<<20)
31 :
32 : FD_FN_CONST static int
33 9 : parse_log_level( char const * level ) {
34 9 : if( FD_UNLIKELY( !strcmp( level, "DEBUG" ) ) ) return 0;
35 9 : if( FD_UNLIKELY( !strcmp( level, "INFO" ) ) ) return 1;
36 6 : if( FD_UNLIKELY( !strcmp( level, "NOTICE" ) ) ) return 2;
37 3 : if( FD_UNLIKELY( !strcmp( level, "WARNING" ) ) ) return 3;
38 0 : if( FD_UNLIKELY( !strcmp( level, "ERR" ) ) ) return 4;
39 0 : if( FD_UNLIKELY( !strcmp( level, "CRIT" ) ) ) return 5;
40 0 : if( FD_UNLIKELY( !strcmp( level, "ALERT" ) ) ) return 6;
41 0 : if( FD_UNLIKELY( !strcmp( level, "EMERG" ) ) ) return 7;
42 0 : return -1;
43 0 : }
44 :
45 : /* TODO: Rewrite this ... */
46 :
47 : void
48 : replace( char * in,
49 : const char * pat,
50 18 : const char * sub ) {
51 18 : char * replace = strstr( in, pat );
52 18 : if( FD_LIKELY( replace ) ) {
53 6 : ulong pat_len = strlen( pat );
54 6 : ulong sub_len = strlen( sub );
55 6 : ulong in_len = strlen( in );
56 6 : if( FD_UNLIKELY( pat_len > in_len ) ) return;
57 :
58 6 : ulong total_len = in_len - pat_len + sub_len;
59 6 : if( FD_UNLIKELY( total_len >= PATH_MAX ) )
60 0 : FD_LOG_ERR(( "configuration scratch directory path too long: `%s`", in ));
61 :
62 6 : uchar after[PATH_MAX] = {0};
63 6 : fd_memcpy( after, replace + pat_len, strlen( replace + pat_len ) );
64 6 : fd_memcpy( replace, sub, sub_len );
65 6 : ulong after_len = strlen( ( const char * ) after );
66 6 : fd_memcpy( replace + sub_len, after, after_len );
67 6 : in[ total_len ] = '\0';
68 6 : }
69 18 : }
70 :
71 : static void
72 3 : validate_ports( config_t * result ) {
73 3 : char dynamic_port_range[ 32 ];
74 3 : fd_memcpy( dynamic_port_range, result->dynamic_port_range, sizeof(dynamic_port_range) );
75 :
76 3 : char * dash = strstr( dynamic_port_range, "-" );
77 3 : if( FD_UNLIKELY( !dash ) )
78 0 : FD_LOG_ERR(( "configuration specifies invalid [dynamic_port_range] `%s`. "
79 3 : "This must be formatted like `<min>-<max>`",
80 3 : result->dynamic_port_range ));
81 :
82 3 : *dash = '\0';
83 3 : char * endptr;
84 3 : ulong agave_port_min = strtoul( dynamic_port_range, &endptr, 10 );
85 3 : if( FD_UNLIKELY( *endptr != '\0' || agave_port_min > USHORT_MAX ) )
86 0 : FD_LOG_ERR(( "configuration specifies invalid [dynamic_port_range] `%s`. "
87 3 : "This must be formatted like `<min>-<max>`",
88 3 : result->dynamic_port_range ));
89 3 : ulong agave_port_max = strtoul( dash + 1, &endptr, 10 );
90 3 : if( FD_UNLIKELY( *endptr != '\0' || agave_port_max > USHORT_MAX ) )
91 0 : FD_LOG_ERR(( "configuration specifies invalid [dynamic_port_range] `%s`. "
92 3 : "This must be formatted like `<min>-<max>`",
93 3 : result->dynamic_port_range ));
94 3 : if( FD_UNLIKELY( agave_port_min > agave_port_max ) )
95 0 : FD_LOG_ERR(( "configuration specifies invalid [dynamic_port_range] `%s`. "
96 3 : "The minimum port must be less than or equal to the maximum port",
97 3 : result->dynamic_port_range ));
98 :
99 3 : if( FD_UNLIKELY( result->tiles.quic.regular_transaction_listen_port >= agave_port_min &&
100 3 : result->tiles.quic.regular_transaction_listen_port < agave_port_max ) )
101 0 : FD_LOG_ERR(( "configuration specifies invalid [tiles.quic.transaction_listen_port] `%hu`. "
102 3 : "This must be outside the dynamic port range `%s`",
103 3 : result->tiles.quic.regular_transaction_listen_port,
104 3 : result->dynamic_port_range ));
105 :
106 3 : if( FD_UNLIKELY( result->tiles.quic.quic_transaction_listen_port >= agave_port_min &&
107 3 : result->tiles.quic.quic_transaction_listen_port < agave_port_max ) )
108 0 : FD_LOG_ERR(( "configuration specifies invalid [tiles.quic.quic_transaction_listen_port] `%hu`. "
109 3 : "This must be outside the dynamic port range `%s`",
110 3 : result->tiles.quic.quic_transaction_listen_port,
111 3 : result->dynamic_port_range ));
112 :
113 3 : if( FD_UNLIKELY( result->tiles.shred.shred_listen_port >= agave_port_min &&
114 3 : result->tiles.shred.shred_listen_port < agave_port_max ) )
115 0 : FD_LOG_ERR(( "configuration specifies invalid [tiles.shred.shred_listen_port] `%hu`. "
116 3 : "This must be outside the dynamic port range `%s`",
117 3 : result->tiles.shred.shred_listen_port,
118 3 : result->dynamic_port_range ));
119 3 : }
120 :
121 : static void
122 : fdctl_cfg_load_buf( config_t * out,
123 : char const * buf,
124 : ulong sz,
125 3 : char const * path ) {
126 :
127 3 : static uchar pod_mem[ 1UL<<30 ];
128 3 : uchar * pod = fd_pod_join( fd_pod_new( pod_mem, sizeof(pod_mem) ) );
129 :
130 3 : fd_toml_err_info_t toml_err[1];
131 3 : uchar scratch[ 4096 ];
132 3 : int toml_errc = fd_toml_parse( buf, sz, pod, scratch, sizeof(scratch), toml_err );
133 3 : if( FD_UNLIKELY( toml_errc!=FD_TOML_SUCCESS ) ) {
134 : /* Override the default error messages of fd_toml for a better user
135 : experience */
136 0 : switch( toml_errc ) {
137 0 : case FD_TOML_ERR_POD:
138 0 : FD_LOG_ERR(( "Failed to parse config file (%s): ran out of buffer space while parsing (Increase FD_TOML_POD_SZ?)", path ));
139 0 : break;
140 0 : case FD_TOML_ERR_SCRATCH:
141 0 : FD_LOG_ERR(( "Failed to parse config file (%s) at line %lu: ran out of scratch space while parsing", path, toml_err->line ));
142 0 : break;
143 0 : case FD_TOML_ERR_KEY:
144 0 : FD_LOG_ERR(( "Failed to parse config file (%s) at line %lu: oversize key", path, toml_err->line ));
145 0 : break;
146 0 : case FD_TOML_ERR_DUP:
147 0 : FD_LOG_ERR(( "Failed to parse config file (%s) at line %lu: duplicate key", path, toml_err->line ));
148 0 : break;
149 0 : case FD_TOML_ERR_RANGE:
150 0 : FD_LOG_ERR(( "Failed to parse config file (%s) at line %lu: invalid value for key", path, toml_err->line ));
151 0 : break;
152 0 : case FD_TOML_ERR_PARSE:
153 0 : FD_LOG_ERR(( "Failed to parse config file (%s) at line %lu", path, toml_err->line ));
154 0 : break;
155 0 : default:
156 0 : FD_LOG_ERR(( "Failed to parse config file (%s): %s", path, fd_toml_strerror( toml_errc ) ));
157 0 : break;
158 0 : }
159 0 : }
160 :
161 3 : if( FD_UNLIKELY( !fdctl_pod_to_cfg( out, pod ) ) ) {
162 0 : FD_LOG_ERR(( "Invalid config (%s)", path ));
163 0 : }
164 :
165 3 : fd_pod_delete( fd_pod_leave( pod ) );
166 3 : }
167 :
168 : static void
169 : fdctl_cfg_load_file( config_t * out,
170 0 : char const * path ) {
171 :
172 0 : int fd = open( path, O_RDONLY );
173 0 : if( FD_UNLIKELY( fd<0 ) ) {
174 0 : FD_LOG_ERR(( "open(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
175 0 : }
176 :
177 0 : struct stat st;
178 0 : if( FD_UNLIKELY( fstat( fd, &st ) ) ) {
179 0 : FD_LOG_ERR(( "fstat(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
180 0 : }
181 0 : ulong toml_sz = (ulong)st.st_size;
182 :
183 0 : if( FD_UNLIKELY( toml_sz==0UL ) ) {
184 0 : if( FD_UNLIKELY( 0!=close( fd ) ) ) {
185 0 : FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
186 0 : }
187 0 : return;
188 0 : }
189 :
190 0 : void * mem = mmap( NULL, toml_sz, PROT_READ, MAP_PRIVATE, fd, 0 );
191 0 : if( FD_UNLIKELY( mem==MAP_FAILED ) ) {
192 0 : FD_LOG_ERR(( "mmap(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
193 0 : }
194 :
195 0 : if( FD_UNLIKELY( 0!=close( fd ) ) ) {
196 0 : FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
197 0 : }
198 :
199 0 : fdctl_cfg_load_buf( out, mem, toml_sz, path );
200 :
201 0 : if( FD_UNLIKELY( 0!=munmap( mem, toml_sz ) ) ) {
202 0 : FD_LOG_ERR(( "munmap(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
203 0 : }
204 0 : }
205 :
206 : void
207 : fdctl_cfg_from_env( int * pargc,
208 : char *** pargv,
209 3 : config_t * config ) {
210 :
211 3 : memset( config, 0, sizeof(config_t) );
212 3 : fdctl_cfg_load_buf( config, (char const *)fdctl_default_config, fdctl_default_config_sz, "default" );
213 : #if FD_HAS_NO_AGAVE
214 : fdctl_cfg_load_buf( config, (char const *)fdctl_default_firedancer_config, fdctl_default_firedancer_config_sz, "default_firedancer" );
215 : #endif
216 :
217 3 : const char * user_config = fd_env_strip_cmdline_cstr(
218 3 : pargc,
219 3 : pargv,
220 3 : "--config",
221 3 : "FIREDANCER_CONFIG_TOML",
222 3 : NULL );
223 :
224 3 : if( FD_LIKELY( user_config ) ) {
225 0 : fdctl_cfg_load_file( config, user_config );
226 0 : }
227 :
228 3 : int netns = fd_env_strip_cmdline_contains( pargc, pargv, "--netns" );
229 3 : if( FD_UNLIKELY( netns ) ) {
230 0 : config->development.netns.enabled = 1;
231 0 : strncpy( config->tiles.net.interface,
232 0 : config->development.netns.interface0,
233 0 : sizeof(config->tiles.net.interface) );
234 0 : config->tiles.net.interface[ sizeof(config->tiles.net.interface) - 1 ] = '\0';
235 0 : }
236 :
237 3 : if( FD_UNLIKELY( !strcmp( config->user, "" ) ) ) {
238 3 : const char * user = fd_sys_util_login_user();
239 3 : if( FD_UNLIKELY( !user ) ) FD_LOG_ERR(( "could not automatically determine a user to run Firedancer as. You must specify a [user] in your configuration TOML file." ));
240 3 : if( FD_UNLIKELY( strlen( user ) >= sizeof( config->user ) ) )
241 0 : FD_LOG_ERR(( "user name `%s` is too long", user ));
242 3 : strncpy( config->user, user, 256 );
243 3 : }
244 :
245 3 : struct utsname utsname;
246 3 : if( FD_UNLIKELY( -1==uname( &utsname ) ) )
247 0 : FD_LOG_ERR(( "could not get uname (%i-%s)", errno, fd_io_strerror( errno ) ));
248 3 : strncpy( config->hostname, utsname.nodename, sizeof(config->hostname) );
249 3 : config->hostname[ sizeof(config->hostname)-1UL ] = '\0'; /* Just truncate the name if it's too long to fit */
250 :
251 3 : ulong cluster = fd_genesis_cluster_identify( config->consensus.expected_genesis_hash );
252 3 : config->is_live_cluster = cluster != FD_CLUSTER_UNKNOWN;
253 :
254 3 : fdctl_cfg_net_auto( config );
255 :
256 3 : if( FD_UNLIKELY( -1==fd_sys_util_user_to_uid( config->user, &config->uid, &config->gid ) ) )
257 0 : FD_LOG_ERR(( "configuration file wants firedancer to run as user `%s` but it does not exist", config->user ));
258 :
259 3 : if( FD_UNLIKELY( !config->uid || !config->gid ) )
260 0 : FD_LOG_ERR(( "firedancer cannot run as root. please specify a non-root user in the configuration file" ));
261 :
262 3 : if( FD_UNLIKELY( getuid() != 0 && config->uid != getuid() ) )
263 0 : FD_LOG_ERR(( "running as uid %u, but config specifies uid %u", getuid(), config->uid ));
264 3 : if( FD_UNLIKELY( getgid() != 0 && config->gid != getgid() ) )
265 0 : FD_LOG_ERR(( "running as gid %u, but config specifies gid %u", getgid(), config->gid ));
266 :
267 3 : ulong len = strlen( config->hugetlbfs.mount_path );
268 3 : if( FD_UNLIKELY( !len ) ) FD_LOG_ERR(( "[hugetlbfs.mount_path] must be non-empty in your configuration file" ));
269 3 : FD_TEST( fd_cstr_printf_check( config->hugetlbfs.gigantic_page_mount_path,
270 3 : sizeof(config->hugetlbfs.gigantic_page_mount_path),
271 3 : NULL,
272 3 : "%s/.gigantic",
273 3 : config->hugetlbfs.mount_path ) );
274 3 : FD_TEST( fd_cstr_printf_check( config->hugetlbfs.huge_page_mount_path,
275 3 : sizeof(config->hugetlbfs.huge_page_mount_path),
276 3 : NULL,
277 3 : "%s/.huge",
278 3 : config->hugetlbfs.mount_path ) );
279 :
280 3 : ulong max_page_sz = fd_cstr_to_shmem_page_sz( config->hugetlbfs.max_page_size );
281 3 : if( FD_UNLIKELY( max_page_sz!=FD_SHMEM_HUGE_PAGE_SZ && max_page_sz!=FD_SHMEM_GIGANTIC_PAGE_SZ ) ) {
282 0 : FD_LOG_ERR(( "[hugetlbfs.max_page_size] must be \"huge\" or \"gigantic\"" ));
283 0 : }
284 :
285 3 : replace( config->log.path, "{user}", config->user );
286 3 : replace( config->log.path, "{name}", config->name );
287 3 : if( FD_LIKELY( !strcmp( "auto", config->log.colorize ) ) ) config->log.colorize1 = 2;
288 0 : else if( FD_LIKELY( !strcmp( "true", config->log.colorize ) ) ) config->log.colorize1 = 1;
289 0 : else if( FD_LIKELY( !strcmp( "false", config->log.colorize ) ) ) config->log.colorize1 = 0;
290 0 : else FD_LOG_ERR(( "[log.colorize] must be one of \"auto\", \"true\", or \"false\"" ));
291 :
292 3 : if( FD_LIKELY( 2==config->log.colorize1 ) ) {
293 3 : char const * cstr = fd_env_strip_cmdline_cstr( NULL, NULL, NULL, "COLORTERM", NULL );
294 3 : int truecolor = cstr && !strcmp( cstr, "truecolor" );
295 :
296 3 : cstr = fd_env_strip_cmdline_cstr( NULL, NULL, NULL, "TERM", NULL );
297 3 : int xterm256color = cstr && !strcmp( cstr, "xterm-256color" );
298 :
299 3 : config->log.colorize1 = truecolor || xterm256color;
300 3 : }
301 :
302 3 : config->log.level_logfile1 = parse_log_level( config->log.level_logfile );
303 3 : config->log.level_stderr1 = parse_log_level( config->log.level_stderr );
304 3 : config->log.level_flush1 = parse_log_level( config->log.level_flush );
305 3 : if( FD_UNLIKELY( -1==config->log.level_logfile1 ) ) FD_LOG_ERR(( "unrecognized [log.level_logfile] `%s`", config->log.level_logfile ));
306 3 : if( FD_UNLIKELY( -1==config->log.level_stderr1 ) ) FD_LOG_ERR(( "unrecognized [log.level_stderr] `%s`", config->log.level_logfile ));
307 3 : if( FD_UNLIKELY( -1==config->log.level_flush1 ) ) FD_LOG_ERR(( "unrecognized [log.level_flush] `%s`", config->log.level_logfile ));
308 :
309 3 : replace( config->scratch_directory, "{user}", config->user );
310 3 : replace( config->scratch_directory, "{name}", config->name );
311 :
312 3 : if( FD_UNLIKELY( strcmp( config->ledger.path, "" ) ) ) {
313 0 : replace( config->ledger.path, "{user}", config->user );
314 0 : replace( config->ledger.path, "{name}", config->name );
315 3 : } else {
316 3 : FD_TEST( fd_cstr_printf_check( config->ledger.path, sizeof(config->ledger.path), NULL, "%s/ledger", config->scratch_directory ) );
317 3 : }
318 :
319 3 : if( FD_UNLIKELY( strcmp( config->snapshots.path, "" ) ) ) {
320 0 : replace( config->snapshots.path, "{user}", config->user );
321 0 : replace( config->snapshots.path, "{name}", config->name );
322 3 : } else {
323 3 : strncpy( config->snapshots.path, config->ledger.path, sizeof(config->snapshots.path) );
324 3 : }
325 :
326 3 : if( FD_UNLIKELY( !strcmp( config->consensus.identity_path, "" ) ) ) {
327 3 : FD_TEST( fd_cstr_printf_check( config->consensus.identity_path,
328 3 : sizeof(config->consensus.identity_path),
329 3 : NULL,
330 3 : "%s/identity.json",
331 3 : config->scratch_directory ) );
332 3 : } else {
333 0 : replace( config->consensus.identity_path, "{user}", config->user );
334 0 : replace( config->consensus.identity_path, "{name}", config->name );
335 0 : }
336 :
337 : #if FD_HAS_NO_AGAVE
338 : if( FD_UNLIKELY( !strcmp( config->consensus.vote_account_path, "" ) ) ) {
339 : FD_TEST( fd_cstr_printf_check( config->consensus.vote_account_path,
340 : sizeof(config->consensus.vote_account_path),
341 : NULL,
342 : "%s/vote-account.json",
343 : config->scratch_directory ) );
344 : }
345 : #endif
346 3 : replace( config->consensus.vote_account_path, "{user}", config->user );
347 3 : replace( config->consensus.vote_account_path, "{name}", config->name );
348 :
349 3 : for( ulong i=0UL; i<config->consensus.authorized_voter_paths_cnt; i++ ) {
350 0 : replace( config->consensus.authorized_voter_paths[ i ], "{user}", config->user );
351 0 : replace( config->consensus.authorized_voter_paths[ i ], "{name}", config->name );
352 0 : }
353 :
354 3 : strcpy( config->cluster, fd_genesis_cluster_name( cluster ) );
355 :
356 : #if FD_HAS_NO_AGAVE
357 : if( FD_UNLIKELY( config->is_live_cluster && cluster!=FD_CLUSTER_TESTNET ) )
358 : FD_LOG_ERR(( "Attempted to start against live cluster `%s`. Firedancer is not "
359 : "ready for production deployment, has not been tested, and is "
360 : "missing consensus critical functionality. Joining a live Solana "
361 : "cluster may destabilize the network. Please do not attempt. You "
362 : "can start against the testnet cluster by specifying the testnet "
363 : "entrypoints from https://docs.solana.com/clusters under "
364 : "[gossip.entrypoints] in your configuration file.", fd_genesis_cluster_name( cluster ) ));
365 : #endif /* FD_HAS_NO_AGAVE */
366 :
367 3 : if( FD_LIKELY( config->is_live_cluster) ) {
368 0 : if( FD_UNLIKELY( !config->development.sandbox ) )
369 0 : FD_LOG_ERR(( "trying to join a live cluster, but configuration disables the sandbox which is a a development only feature" ));
370 0 : if( FD_UNLIKELY( config->development.no_clone ) )
371 0 : FD_LOG_ERR(( "trying to join a live cluster, but configuration disables multiprocess which is a development only feature" ));
372 0 : if( FD_UNLIKELY( config->development.netns.enabled ) )
373 0 : FD_LOG_ERR(( "trying to join a live cluster, but configuration enables [development.netns] which is a development only feature" ));
374 0 : if( FD_UNLIKELY( config->development.bench.larger_max_cost_per_block ) )
375 0 : FD_LOG_ERR(( "trying to join a live cluster, but configuration enables [development.bench.larger_max_cost_per_block] which is a development only feature" ));
376 0 : if( FD_UNLIKELY( config->development.bench.larger_shred_limits_per_block ) )
377 0 : FD_LOG_ERR(( "trying to join a live cluster, but configuration enables [development.bench.larger_shred_limits_per_block] which is a development only feature" ));
378 0 : if( FD_UNLIKELY( config->development.bench.disable_blockstore_from_slot ) )
379 0 : FD_LOG_ERR(( "trying to join a live cluster, but configuration has a non-zero value for [development.bench.disable_blockstore_from_slot] which is a development only feature" ));
380 0 : if( FD_UNLIKELY( config->development.bench.disable_status_cache ) )
381 0 : FD_LOG_ERR(( "trying to join a live cluster, but configuration enables [development.bench.disable_status_cache] which is a development only feature" ));
382 0 : }
383 :
384 3 : if( FD_UNLIKELY( config->tiles.quic.quic_transaction_listen_port != config->tiles.quic.regular_transaction_listen_port + 6 ) )
385 0 : FD_LOG_ERR(( "configuration specifies invalid [tiles.quic.quic_transaction_listen_port] `%hu`. "
386 3 : "This must be 6 more than [tiles.quic.regular_transaction_listen_port] `%hu`",
387 3 : config->tiles.quic.quic_transaction_listen_port,
388 3 : config->tiles.quic.regular_transaction_listen_port ));
389 :
390 3 : if( FD_LIKELY( !strcmp( config->consensus.identity_path, "" ) ) ) {
391 0 : if( FD_UNLIKELY( config->is_live_cluster ) )
392 0 : FD_LOG_ERR(( "configuration file must specify [consensus.identity_path] when joining a live cluster" ));
393 :
394 0 : FD_TEST( fd_cstr_printf_check( config->consensus.identity_path,
395 0 : sizeof(config->consensus.identity_path),
396 0 : NULL,
397 0 : "%s/identity.json",
398 0 : config->scratch_directory ) );
399 0 : }
400 :
401 3 : fdctl_cfg_validate( config );
402 3 : validate_ports( config );
403 3 : }
404 :
405 : void
406 3 : fdctl_cfg_net_auto( config_t * config ) {
407 :
408 3 : if( FD_UNLIKELY( !strcmp( config->tiles.net.interface, "" ) && !config->development.netns.enabled ) ) {
409 3 : uint ifindex;
410 3 : int result = fd_net_util_internet_ifindex( &ifindex );
411 3 : if( FD_UNLIKELY( -1==result && errno!=ENODEV ) ) FD_LOG_ERR(( "could not get network device index (%i-%s)", errno, fd_io_strerror( errno ) ));
412 3 : else if( FD_UNLIKELY( -1==result ) )
413 0 : FD_LOG_ERR(( "no network device found which routes to 8.8.8.8. If no network "
414 3 : "interface is specified in the configuration file, Firedancer "
415 3 : "tries to use the first network interface found which routes to "
416 3 : "8.8.8.8. You can see what this is by running `ip route get 8.8.8.8` "
417 3 : "You can fix this error by specifying a network interface to bind to in "
418 3 : "your configuration file under [net.interface]" ));
419 :
420 3 : if( FD_UNLIKELY( !if_indextoname( ifindex, config->tiles.net.interface ) ) )
421 0 : FD_LOG_ERR(( "could not get name of interface with index %u", ifindex ));
422 3 : }
423 :
424 3 : if( FD_UNLIKELY( config->development.netns.enabled ) ) {
425 :
426 0 : if( !strcmp( config->tiles.net.interface, "" ) ) {
427 0 : memcpy( config->tiles.net.interface, config->development.netns.interface0, sizeof(config->tiles.net.interface) );
428 0 : }
429 :
430 0 : if( !strcmp( config->development.pktgen.fake_dst_ip, "" ) ) {
431 0 : memcpy( config->development.pktgen.fake_dst_ip, config->development.netns.interface1_addr, sizeof(config->development.netns.interface1_addr) );
432 0 : }
433 :
434 0 : if( FD_UNLIKELY( strcmp( config->development.netns.interface0, config->tiles.net.interface ) ) ) {
435 0 : FD_LOG_ERR(( "netns interface and firedancer interface are different. If you are using the "
436 0 : "[development.netns] functionality to run Firedancer in a network namespace "
437 0 : "for development, the configuration file must specify that "
438 0 : "[development.netns.interface0] is the same as [tiles.net.interface]" ));
439 0 : }
440 :
441 0 : if( FD_UNLIKELY( !fd_cstr_to_ip4_addr( config->development.netns.interface0_addr, &config->tiles.net.ip_addr ) ) )
442 0 : FD_LOG_ERR(( "configuration specifies invalid netns IP address `%s`", config->development.netns.interface0_addr ));
443 :
444 3 : } else { /* !config->development.netns.enabled */
445 :
446 3 : if( FD_UNLIKELY( !if_nametoindex( config->tiles.net.interface ) ) )
447 0 : FD_LOG_ERR(( "configuration specifies network interface `%s` which does not exist", config->tiles.net.interface ));
448 3 : uint iface_ip;
449 3 : if( FD_UNLIKELY( -1==fd_net_util_if_addr( config->tiles.net.interface, &iface_ip ) ) )
450 0 : FD_LOG_ERR(( "could not get IP address for interface `%s`", config->tiles.net.interface ));
451 :
452 3 : if( FD_UNLIKELY( strcmp( config->gossip.host, "" ) ) ) {
453 0 : uint gossip_ip_addr = iface_ip;
454 0 : int has_gossip_ip4 = 0;
455 0 : if( FD_UNLIKELY( strlen( config->gossip.host )<=15UL ) ) {
456 : /* Only sets gossip_ip_addr if it's a valid IPv4 address, otherwise assume it's a DNS name */
457 0 : has_gossip_ip4 = fd_cstr_to_ip4_addr( config->gossip.host, &gossip_ip_addr );
458 0 : }
459 0 : if( FD_UNLIKELY( !fd_ip4_addr_is_public( gossip_ip_addr ) && config->is_live_cluster && has_gossip_ip4 ) )
460 0 : FD_LOG_ERR(( "Trying to use [gossip.host] " FD_IP4_ADDR_FMT " for listening to incoming "
461 0 : "transactions, but it is part of a private network and will not be routable "
462 0 : "for other Solana network nodes.",
463 0 : FD_IP4_ADDR_FMT_ARGS( iface_ip ) ));
464 3 : } else if( FD_UNLIKELY( !fd_ip4_addr_is_public( iface_ip ) && config->is_live_cluster ) ) {
465 0 : FD_LOG_ERR(( "Trying to use network interface `%s` for listening to incoming transactions, "
466 0 : "but it has IPv4 address " FD_IP4_ADDR_FMT " which is part of a private network "
467 0 : "and will not be routable for other Solana network nodes. If you are running "
468 0 : "behind a NAT and this interface is publicly reachable, you can continue by "
469 0 : "manually specifying the IP address to advertise in your configuration under "
470 0 : "[gossip.host].",
471 0 : config->tiles.net.interface, FD_IP4_ADDR_FMT_ARGS( iface_ip ) ));
472 0 : }
473 :
474 3 : config->tiles.net.ip_addr = iface_ip;
475 :
476 3 : }
477 :
478 3 : }
479 :
480 : int
481 0 : fdctl_cfg_to_memfd( config_t const * config ) {
482 0 : int config_memfd = memfd_create( "fd_config", 0 );
483 0 : if( FD_UNLIKELY( -1==config_memfd ) ) FD_LOG_ERR(( "memfd_create() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
484 0 : if( FD_UNLIKELY( -1==ftruncate( config_memfd, sizeof( config_t ) ) ) ) FD_LOG_ERR(( "ftruncate() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
485 :
486 0 : uchar * bytes = mmap( NULL, sizeof( config_t ), PROT_READ | PROT_WRITE, MAP_SHARED, config_memfd, 0 );
487 0 : if( FD_UNLIKELY( bytes == MAP_FAILED ) ) FD_LOG_ERR(( "mmap() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
488 0 : fd_memcpy( bytes, config, sizeof( config_t ) );
489 0 : if( FD_UNLIKELY( munmap( bytes, sizeof( config_t ) ) ) ) FD_LOG_ERR(( "munmap() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
490 :
491 0 : return config_memfd;
492 0 : }
|