Line data Source code
1 : #define _GNU_SOURCE
2 : #include "../../../shared/commands/configure/configure.h"
3 :
4 : #include <errno.h>
5 : #include <sys/stat.h>
6 :
7 0 : #define NAME "netns"
8 :
9 : static int
10 0 : enabled( config_t const * config ) {
11 0 : return config->development.netns.enabled;
12 0 : }
13 :
14 : static void
15 : init_perm( fd_cap_chk_t * chk,
16 0 : config_t const * config FD_PARAM_UNUSED ) {
17 0 : fd_cap_chk_root( chk, NAME, "create and enter network namespaces" );
18 0 : }
19 :
20 : static void
21 : fini_perm( fd_cap_chk_t * chk,
22 0 : config_t const * config FD_PARAM_UNUSED ) {
23 0 : fd_cap_chk_root( chk, NAME, "remove network namespaces" );
24 0 : }
25 :
26 : /* RUN() executes the given string and formatting arguments as a
27 : subprocess, and waits for the child to complete. If the child does
28 : not exit successfully with code 0, the calling program is aborted. */
29 :
30 0 : #define RUN(...) do { \
31 0 : char cmd[ 4096 ]; \
32 0 : FD_TEST( fd_cstr_printf_check( cmd, \
33 0 : sizeof(cmd), \
34 0 : NULL, \
35 0 : __VA_ARGS__ ) ); \
36 0 : int ret = system( cmd ); \
37 0 : if( FD_UNLIKELY( ret ) ) \
38 0 : FD_LOG_ERR(( "running command `%s` failed exit code=%d (%i-%s)", \
39 0 : cmd, \
40 0 : ret, \
41 0 : errno, \
42 0 : fd_io_strerror( errno ) )); \
43 0 : } while( 0 )
44 :
45 : static void
46 0 : init( config_t const * config ) {
47 0 : uint tiles = config->layout.net_tile_count;
48 0 : const char * interface0 = config->development.netns.interface0;
49 0 : const char * interface1 = config->development.netns.interface1;
50 :
51 0 : RUN( "ip netns add %s", interface0 );
52 0 : RUN( "ip netns add %s", interface1 );
53 0 : RUN( "ip link add dev %s netns %s type veth peer name %s netns %s numrxqueues %u numtxqueues %u",
54 0 : interface0, interface0, interface1, interface1, tiles, tiles );
55 0 : RUN( "ip netns exec %s ip link set dev %s address %s",
56 0 : interface0, interface0, config->development.netns.interface0_mac );
57 0 : RUN( "ip netns exec %s ip link set dev %s address %s",
58 0 : interface1, interface1, config->development.netns.interface1_mac );
59 0 : RUN( "ip netns exec %s ip address add %s/30 dev %s scope link",
60 0 : interface0, config->development.netns.interface0_addr, interface0 );
61 0 : RUN( "ip netns exec %s ip address add %s/30 dev %s scope link",
62 0 : interface1, config->development.netns.interface1_addr, interface1 );
63 0 : RUN( "ip netns exec %s ip link set dev %s up", interface0, interface0 );
64 0 : RUN( "ip netns exec %s ip link set dev %s up", interface1, interface1 );
65 :
66 : /* we need one channel for both TX and RX on the NIC for each QUIC
67 : tile, but the virtual interfaces default to one channel total */
68 0 : RUN( "nsenter --net=/var/run/netns/%s ethtool --set-channels %s rx %u tx %u",
69 0 : interface0, interface0, tiles, tiles );
70 0 : RUN( "nsenter --net=/var/run/netns/%s ethtool --set-channels %s rx %u tx %u",
71 0 : interface1, interface1, tiles, tiles );
72 :
73 : /* UDP segmentation is a kernel feature that batches multiple UDP
74 : packets into one in the kernel before splitting them later when
75 : dispatching. this feature is broken with network namespaces so we
76 : disable it. otherwise, we would see very large packets that don't
77 : decrypt. need on both tx and rx sides */
78 0 : RUN( "nsenter --net=/var/run/netns/%s ethtool -K %s tx-udp-segmentation off",
79 0 : interface0, interface0 );
80 0 : RUN( "nsenter --net=/var/run/netns/%s ethtool -K %s tx-udp-segmentation off",
81 0 : interface1, interface1 );
82 :
83 : /* generic segmentation offload and TX GRE segmentation are similar
84 : things on the tx side that also get messed up under netns in
85 : unknown ways */
86 0 : RUN( "nsenter --net=/var/run/netns/%s ethtool -K %s generic-segmentation-offload off",
87 0 : interface0, interface0 );
88 0 : RUN( "nsenter --net=/var/run/netns/%s ethtool -K %s generic-segmentation-offload off",
89 0 : interface1, interface1 );
90 0 : RUN( "nsenter --net=/var/run/netns/%s ethtool -K %s tx-gre-segmentation off",
91 0 : interface0, interface0 );
92 0 : RUN( "nsenter --net=/var/run/netns/%s ethtool -K %s tx-gre-segmentation off",
93 0 : interface1, interface1 );
94 0 : }
95 :
96 : static void
97 : fini( config_t const * config,
98 0 : int pre_init FD_PARAM_UNUSED ) {
99 0 : const char * interface0 = config->development.netns.interface0;
100 0 : const char * interface1 = config->development.netns.interface1;
101 :
102 0 : char cmd[ 256 ];
103 0 : FD_TEST( fd_cstr_printf_check( cmd, sizeof(cmd), NULL, "ip link del dev %s", interface0 ) );
104 0 : int status3 = system( cmd ); // Destroys interface1 as well, no need to check failure
105 0 : if( FD_UNLIKELY( status3 ) ) FD_LOG_DEBUG(( "ip link del dev %s failed", interface0 ));
106 :
107 0 : FD_TEST( fd_cstr_printf_check( cmd, sizeof(cmd), NULL, "ip netns delete %s", interface0 ) );
108 0 : int status1 = system( cmd );
109 0 : FD_TEST( fd_cstr_printf_check( cmd, sizeof(cmd), NULL, "ip netns delete %s", interface1 ) );
110 0 : int status2 = system( cmd );
111 :
112 : /* if neither of them was present, we wouldn't get to the undo step so make sure we were
113 : able to delete whatever is there */
114 0 : if( FD_UNLIKELY( status1 && status2 ) ) FD_LOG_ERR(( "failed to delete network namespaces" ));
115 0 : }
116 :
117 : static configure_result_t
118 0 : check( config_t const * config ) {
119 0 : const char * interface0 = config->development.netns.interface0;
120 0 : const char * interface1 = config->development.netns.interface1;
121 :
122 0 : char path[ PATH_MAX ];
123 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/var/run/netns/%s", interface0 ) );
124 :
125 0 : struct stat st;
126 0 : int result1 = stat( path, &st );
127 0 : if( FD_UNLIKELY( result1 && errno != ENOENT ) ) PARTIALLY_CONFIGURED( "netns `%s` cannot be read", interface0 );
128 :
129 0 : FD_TEST( fd_cstr_printf_check( path, sizeof(path), NULL, "/var/run/netns/%s", interface1 ) );
130 0 : int result2 = stat( path, &st );
131 0 : if( FD_UNLIKELY( result2 && errno != ENOENT ) ) PARTIALLY_CONFIGURED( "netns `%s` cannot be read", interface1 );
132 :
133 0 : if( FD_UNLIKELY( result1 && result2 ) ) NOT_CONFIGURED( "netns `%s` and `%s` do not exist", interface0, interface1 );
134 0 : else if( FD_UNLIKELY( result1 ) ) NOT_CONFIGURED( "netns `%s` does not exist", interface0 );
135 0 : else if( FD_UNLIKELY( result2 ) ) NOT_CONFIGURED( "netns `%s` does not exist", interface1 );
136 :
137 : /* todo: use `ip netns exec`, `ip link show` to verify the
138 : configuration is correct TODO: Check the ethtool stuff is correct
139 : as well */
140 0 : CONFIGURE_OK();
141 0 : }
142 :
143 : configure_stage_t fd_cfg_stage_netns = {
144 : .name = NAME,
145 : .always_recreate = 0,
146 : .enabled = enabled,
147 : .init_perm = init_perm,
148 : .fini_perm = fini_perm,
149 : .init = init,
150 : .fini = fini,
151 : .check = check,
152 : };
153 :
154 : #undef NAME
|