diff --git a/src/app/fdctl/config.c b/src/app/fdctl/config.c index 32f1c2b356..4df784ecf4 100644 --- a/src/app/fdctl/config.c +++ b/src/app/fdctl/config.c @@ -526,12 +526,20 @@ fdctl_cfg_from_env( int * pargc, config->is_live_cluster = cluster != FD_CLUSTER_UNKNOWN; if( FD_UNLIKELY( config->development.netns.enabled ) ) { - /* not currently supporting multihoming on netns */ - if( FD_UNLIKELY( strcmp( config->development.netns.interface0, config->tiles.net.interface ) ) ) + if( !strcmp( config->tiles.net.interface, "" ) ) { + memcpy( config->tiles.net.interface, config->development.netns.interface0, sizeof(config->tiles.net.interface) ); + } + + if( !strcmp( config->development.pktgen.fake_dst_ip, "" ) ) { + memcpy( config->development.pktgen.fake_dst_ip, config->development.netns.interface1_addr, sizeof(config->development.netns.interface1_addr) ); + } + + if( FD_UNLIKELY( strcmp( config->development.netns.interface0, config->tiles.net.interface ) ) ) { FD_LOG_ERR(( "netns interface and firedancer interface are different. If you are using the " "[development.netns] functionality to run Firedancer in a network namespace " "for development, the configuration file must specify that " - "[development.netns.interface0] is the same as [net.interface]" )); + "[development.netns.interface0] is the same as [tiles.net.interface]" )); + } if( FD_UNLIKELY( !fd_cstr_to_ip4_addr( config->development.netns.interface0_addr, &config->tiles.net.ip_addr ) ) ) FD_LOG_ERR(( "configuration specifies invalid netns IP address `%s`", config->development.netns.interface0_addr )); diff --git a/src/app/fdctl/config.h b/src/app/fdctl/config.h index 382de23715..5cf6aaf354 100644 --- a/src/app/fdctl/config.h +++ b/src/app/fdctl/config.h @@ -175,10 +175,10 @@ struct fdctl_config { int enabled; char interface0 [ 16 ]; char interface0_mac [ 32 ]; - char interface0_addr[ 32 ]; + char interface0_addr[ 16 ]; char interface1 [ 16 ]; char interface1_mac [ 32 ]; - char interface1_addr[ 32 ]; + char interface1_addr[ 16 ]; } netns; struct { diff --git a/src/app/fdctl/netconf.c b/src/app/fdctl/netconf.c index 4aefc68b45..2e3566170c 100644 --- a/src/app/fdctl/netconf.c +++ b/src/app/fdctl/netconf.c @@ -50,11 +50,7 @@ netconf_cmd_fn( args_t * args, fd_fib4_fprintf( fib4_local, stdout ); fd_fib4_leave( fib4_local ); - char if_name[ IF_NAMESIZE ] = "???"; - if( FD_UNLIKELY( !if_indextoname( tile->netlink.neigh_if_idx, if_name ) ) ) { - memcpy( if_name, "???", 4 ); - } - printf( "\nNEIGHBOR TABLE (%u-%s)\n\n", tile->netlink.neigh_if_idx, if_name ); + printf( "\nNEIGHBOR TABLE (%.16s)\n\n", tile->netlink.neigh_if ); fd_neigh4_hmap_t neigh4[1]; FD_TEST( fd_neigh4_hmap_join( neigh4, fd_topo_obj_laddr( topo, tile->netlink.neigh4_obj_id ), fd_topo_obj_laddr( topo, tile->netlink.neigh4_ele_obj_id ) ) ); fd_neigh4_hmap_fprintf( neigh4, stdout ); diff --git a/src/app/fdctl/run/run.c b/src/app/fdctl/run/run.c index 8a457b3f61..2100dc7083 100644 --- a/src/app/fdctl/run/run.c +++ b/src/app/fdctl/run/run.c @@ -687,27 +687,29 @@ extern configure_stage_t fd_cfg_stage_ethtool_loopback; extern configure_stage_t fd_cfg_stage_sysctl; void -fdctl_check_configure( config_t * const config ) { +fdctl_check_configure( config_t * config ) { configure_result_t check = fd_cfg_stage_hugetlbfs.check( config ); if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) ) FD_LOG_ERR(( "Huge pages are not configured correctly: %s. You can run `fdctl configure init hugetlbfs` " "to create the mounts correctly. This must be done after every system restart before running " "Firedancer.", check.message )); - check = fd_cfg_stage_ethtool_channels.check( config ); - if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) ) - FD_LOG_ERR(( "Network %s. You can run `fdctl configure init ethtool-channels` to set the number of channels on the " - "network device correctly.", check.message )); - - check = fd_cfg_stage_ethtool_gro.check( config ); - if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) ) - FD_LOG_ERR(( "Network %s. You can run `fdctl configure init ethtool-gro` to disable generic-receive-offload " - "as required.", check.message )); - - check = fd_cfg_stage_ethtool_loopback.check( config ); - if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) ) - FD_LOG_ERR(( "Network %s. You can run `fdctl configure init ethtool-loopback` to disable tx-udp-segmentation " - "on the loopback device.", check.message )); + if( FD_LIKELY( !config->development.netns.enabled ) ) { + check = fd_cfg_stage_ethtool_channels.check( config ); + if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) ) + FD_LOG_ERR(( "Network %s. You can run `fdctl configure init ethtool-channels` to set the number of channels on the " + "network device correctly.", check.message )); + + check = fd_cfg_stage_ethtool_gro.check( config ); + if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) ) + FD_LOG_ERR(( "Network %s. You can run `fdctl configure init ethtool-gro` to disable generic-receive-offload " + "as required.", check.message )); + + check = fd_cfg_stage_ethtool_loopback.check( config ); + if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) ) + FD_LOG_ERR(( "Network %s. You can run `fdctl configure init ethtool-loopback` to disable tx-udp-segmentation " + "on the loopback device.", check.message )); + } check = fd_cfg_stage_sysctl.check( config ); if( FD_UNLIKELY( check.result!=CONFIGURE_OK ) ) @@ -716,8 +718,8 @@ fdctl_check_configure( config_t * const config ) { } void -run_firedancer_init( config_t * const config, - int init_workspaces ) { +run_firedancer_init( config_t * config, + int init_workspaces ) { struct stat st; int err = stat( config->consensus.identity_path, &st ); if( FD_UNLIKELY( -1==err && errno==ENOENT ) ) FD_LOG_ERR(( "[consensus.identity_path] key does not exist `%s`. You can generate an identity key at this path by running `fdctl keys new identity --config `", config->consensus.identity_path )); @@ -734,6 +736,18 @@ run_firedancer_init( config_t * const config, initialize_stacks( config ); } +void +fdctl_setup_netns( config_t * config ) { + if( config->development.netns.enabled ) { + enter_network_namespace( config->tiles.net.interface ); + close_network_namespace_original_fd(); + } + + fd_cfg_stage_ethtool_channels.init( config ); + fd_cfg_stage_ethtool_gro .init( config ); + fd_cfg_stage_ethtool_loopback.init( config ); +} + /* The boot sequence is a little bit involved... A process tree is created that looks like, diff --git a/src/app/fdctl/run/run.h b/src/app/fdctl/run/run.h index f028c5006f..bf960689c5 100644 --- a/src/app/fdctl/run/run.h +++ b/src/app/fdctl/run/run.h @@ -32,6 +32,9 @@ void run_firedancer_init( config_t * const config, int init_workspaces ); +void +fdctl_setup_netns( config_t * config ); + void run_firedancer( config_t * const config, int parent_pipefd, diff --git a/src/app/fdctl/utility.c b/src/app/fdctl/utility.c index d1592cb509..e14802bf60 100644 --- a/src/app/fdctl/utility.c +++ b/src/app/fdctl/utility.c @@ -11,7 +11,9 @@ #include #include #include +#include /* IFF_UP */ +#include /* ioctl(2) */ #include /* for mprotect */ #include #include @@ -40,6 +42,16 @@ enter_network_namespace( const char * interface ) { FD_LOG_ERR(( "failed to enter network namespace `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) )); int ret = close( fd ); if( FD_UNLIKELY( ret ) ) FD_LOG_ERR(( "enter_network_namespace %d (%i-%s)", ret, errno, fd_io_strerror( errno ) )); + + /* `ip link set dev lo up` + Done via the ioctl API for simplicity. Requires a dummy socket. */ + int ifreq_fd = socket( AF_INET, SOCK_DGRAM, 0 ); + if( FD_UNLIKELY( ifreq_fd<0 ) ) FD_LOG_ERR(( "socket(AF_INET,SOCK_DGRAM,0) failed (%i-%s)", errno, fd_io_strerror( errno ) )); + struct ifreq ifr = { .ifr_name = "lo" }; + if( FD_UNLIKELY( ioctl( ifreq_fd, SIOCGIFFLAGS, &ifr ) ) ) FD_LOG_ERR(( "ioctl(SIOCGIFFLAGS,\"lo\") failed (%i-%s)", errno, fd_io_strerror( errno ) )); + ifr.ifr_flags |= (IFF_UP|IFF_RUNNING); + if( FD_UNLIKELY( ioctl( ifreq_fd, SIOCSIFFLAGS, &ifr ) ) ) FD_LOG_ERR(( "ioctl(SIOCGIFFLAGS,\"lo\",IFF_UP|IFF_RUNNING) failed (%i-%s)", errno, fd_io_strerror( errno ) )); + if( FD_UNLIKELY( close( ifreq_fd ) ) ) FD_LOG_ERR(( "failed to close dummy UDP socket (%i-%s)", errno, fd_io_strerror( errno ) )); } void diff --git a/src/app/fddev/bench.c b/src/app/fddev/bench.c index 9457e3280b..81179fbba4 100644 --- a/src/app/fddev/bench.c +++ b/src/app/fddev/bench.c @@ -169,6 +169,7 @@ bench_cmd_fn( args_t * args, update_config_for_dev( config ); run_firedancer_init( config, 1 ); + fdctl_setup_netns( config ); fd_xdp_fds_t fds = fd_topo_install_xdp( &config->topo ); (void)fds; diff --git a/src/app/fddev/dev.c b/src/app/fddev/dev.c index 9c0209f17a..1a121a8ad5 100644 --- a/src/app/fddev/dev.c +++ b/src/app/fddev/dev.c @@ -150,6 +150,7 @@ run_firedancer_threaded( config_t * config , int init_workspaces) { fd_topo_print_log( 0, &config->topo ); run_firedancer_init( config, init_workspaces ); + fdctl_setup_netns( config ); if( FD_UNLIKELY( config->development.debug_tile ) ) { fd_log_private_shared_lock[ 1 ] = 1; diff --git a/src/app/fddev/pktgen.c b/src/app/fddev/pktgen.c index 2475316b22..564e4d6b26 100644 --- a/src/app/fddev/pktgen.c +++ b/src/app/fddev/pktgen.c @@ -83,6 +83,7 @@ pktgen_cmd_fn( args_t * args, /* FIXME this allocates lots of memory unnecessarily */ initialize_workspaces( config ); initialize_stacks( config ); + fdctl_setup_netns( config ); (void)fd_topo_install_xdp( &config->topo );; fd_topo_join_workspaces( &config->topo, FD_SHMEM_JOIN_MODE_READ_WRITE ); diff --git a/src/ballet/http/fd_http_server.c b/src/ballet/http/fd_http_server.c index 3bf1f74b61..492f12aad7 100644 --- a/src/ballet/http/fd_http_server.c +++ b/src/ballet/http/fd_http_server.c @@ -4,6 +4,7 @@ #include "picohttpparser.h" #include "fd_sha1.h" #include "../base64/fd_base64.h" +#include "../../util/net/fd_ip4.h" #include #include @@ -282,8 +283,12 @@ fd_http_server_listen( fd_http_server_t * http, .sin_addr.s_addr = address, }; - if( FD_UNLIKELY( -1==bind( sockfd, fd_type_pun( &addr ), sizeof( addr ) ) ) ) FD_LOG_ERR(( "bind failed (%i-%s)", errno, strerror( errno ) )); - if( FD_UNLIKELY( -1==listen( sockfd, (int)http->max_conns ) ) ) FD_LOG_ERR(( "listen failed (%i-%s)", errno, strerror( errno ) )); + if( FD_UNLIKELY( -1==bind( sockfd, fd_type_pun( &addr ), sizeof( addr ) ) ) ) { + FD_LOG_ERR(( "bind(%i,AF_INET," FD_IP4_ADDR_FMT ":%u) failed (%i-%s)", + sockfd, FD_IP4_ADDR_FMT_ARGS( address ), port, + errno, fd_io_strerror( errno ) )); + } + if( FD_UNLIKELY( -1==listen( sockfd, (int)http->max_conns ) ) ) FD_LOG_ERR(( "listen failed (%i-%s)", errno, fd_io_strerror( errno ) )); http->socket_fd = sockfd; http->pollfds[ http->max_conns+http->max_ws_conns ].fd = http->socket_fd; diff --git a/src/disco/netlink/fd_netlink_tile.c b/src/disco/netlink/fd_netlink_tile.c index c1b623be2d..4de2638475 100644 --- a/src/disco/netlink/fd_netlink_tile.c +++ b/src/disco/netlink/fd_netlink_tile.c @@ -48,8 +48,6 @@ fd_netlink_topo_create( fd_topo_tile_t * netlink_tile, /* Configure neighbor hashmap: Open addressed hashmap with 3.0 sparsity factor and 16 long probe chain */ - uint const neigh_if_idx = if_nametoindex( config->tiles.net.interface ); - if( FD_UNLIKELY( !neigh_if_idx ) ) FD_LOG_ERR(( "if_nametoindex(%s) failed (%i-%s)", config->tiles.net.interface, errno, fd_io_strerror( errno ) )); ulong const neigh_ele_max = fd_ulong_pow2_up( 3UL * config->tiles.netlink.max_neighbors ); ulong const neigh_ele_align = alignof(fd_neigh4_entry_t); ulong const neigh_ele_fp = neigh_ele_max * sizeof(fd_neigh4_entry_t); @@ -67,7 +65,7 @@ fd_netlink_topo_create( fd_topo_tile_t * netlink_tile, netlink_tile->netlink.netdev_dbl_buf_obj_id = netdev_dbl_buf_obj->id; netlink_tile->netlink.fib4_main_obj_id = fib4_main_obj->id; netlink_tile->netlink.fib4_local_obj_id = fib4_local_obj->id; - netlink_tile->netlink.neigh_if_idx = neigh_if_idx; + memcpy( netlink_tile->netlink.neigh_if, config->tiles.net.interface, sizeof(netlink_tile->netlink.neigh_if) ); netlink_tile->netlink.neigh4_obj_id = neigh4_obj->id; netlink_tile->netlink.neigh4_ele_obj_id = neigh4_ele_obj->id; } @@ -135,10 +133,13 @@ privileged_init( fd_topo_t * topo, FD_LOG_ERR(( "Topology contains more than one netlink tile" )); } + uint const neigh_if_idx = if_nametoindex( tile->netlink.neigh_if ); + if( FD_UNLIKELY( !neigh_if_idx ) ) FD_LOG_ERR(( "if_nametoindex(%.16s) failed (%i-%s)", tile->netlink.neigh_if, errno, fd_io_strerror( errno ) )); + fd_netlink_tile_ctx_t * ctx = fd_topo_obj_laddr( topo, tile->tile_obj_id ); fd_memset( ctx, 0, sizeof(fd_netlink_tile_ctx_t) ); ctx->magic = FD_NETLINK_TILE_CTX_MAGIC; - ctx->neigh4_ifidx = tile->netlink.neigh_if_idx; + ctx->neigh4_ifidx = neigh_if_idx; if( FD_UNLIKELY( !fd_netlink_init( ctx->nl_monitor, 1000U ) ) ) { FD_LOG_ERR(( "Failed to connect to rtnetlink" )); diff --git a/src/disco/topo/fd_topo.h b/src/disco/topo/fd_topo.h index 63bf51bc67..1fa59f768a 100644 --- a/src/disco/topo/fd_topo.h +++ b/src/disco/topo/fd_topo.h @@ -153,7 +153,7 @@ typedef struct { ulong netdev_dbl_buf_obj_id; /* dbl_buf containing netdev_tbl */ ulong fib4_main_obj_id; /* fib4 containing main route table */ ulong fib4_local_obj_id; /* fib4 containing local route table */ - uint neigh_if_idx; /* neigh4 interface index */ + char neigh_if[ 16 ]; /* neigh4 interface name */ ulong neigh4_obj_id; /* neigh4 hash map header */ ulong neigh4_ele_obj_id; /* neigh4 hash map slots */ } netlink; diff --git a/src/waltz/mib/fd_netdev_netlink.c b/src/waltz/mib/fd_netdev_netlink.c index 99ce900d13..8ad72103d1 100644 --- a/src/waltz/mib/fd_netdev_netlink.c +++ b/src/waltz/mib/fd_netdev_netlink.c @@ -120,13 +120,14 @@ fd_netdev_netlink_load_table( fd_netdev_tbl_join_t * tbl, switch( rat->rta_type ) { case IFLA_IFNAME: - if( FD_UNLIKELY( rta_sz==0 || rta_sz>=IFNAMSIZ ) ) { + /* Includes trailing zero */ + if( FD_UNLIKELY( rta_sz==0 || rta_sz>IFNAMSIZ ) ) { FD_LOG_WARNING(( "Error reading interface table: IFLA_IFNAME has unsupported size %lu", rta_sz )); err = EPROTO; goto fail; } memcpy( netdev->name, rta, rta_sz ); - netdev->name[ rta_sz ] = '\0'; + netdev->name[ rta_sz-1 ] = '\0'; break; case IFLA_ADDRESS: