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