Line data Source code
1 : /* This stage disables various ethtool features on the main and
2 : loopback interfaces that are known to be incompatible with AF_XDP.
3 :
4 : - "Generic Receive Offload": If left enabled, GRO will mangle UDP
5 : packets in a way that causes AF_XDP packets to get corrupted.
6 :
7 : - "GRE Segmentation Offload": This feature has been known to cause
8 : corruption of packets sent via normal sockets while XDP is in use
9 : on the same system. */
10 :
11 : #include "configure.h"
12 :
13 : #include <errno.h>
14 : #include <stdio.h>
15 : #include <sys/stat.h>
16 :
17 : #include "fd_ethtool_ioctl.h"
18 :
19 0 : #define NAME "ethtool-offloads"
20 :
21 : static int
22 0 : enabled( fd_config_t const * config ) {
23 :
24 : /* if we're running in a network namespace, we configure ethtool on
25 : the virtual device as part of netns setup, not here */
26 0 : if( config->development.netns.enabled ) return 0;
27 :
28 : /* only enable if network stack is XDP */
29 0 : if( 0!=strcmp( config->net.provider, "xdp" ) ) return 0;
30 :
31 0 : return 1;
32 0 : }
33 :
34 : static void
35 : init_perm( fd_cap_chk_t * chk,
36 0 : fd_config_t const * config FD_PARAM_UNUSED ) {
37 0 : fd_cap_chk_root( chk, NAME, "disable network device features with `ethtool --offload INTF FEATURE off`" );
38 0 : }
39 :
40 : static int
41 0 : device_is_bonded( char const * device ) {
42 0 : char path[ PATH_MAX ];
43 0 : FD_TEST( fd_cstr_printf_check( path, PATH_MAX, NULL, "/sys/class/net/%s/bonding", device ) );
44 0 : struct stat st;
45 0 : int err = stat( path, &st );
46 0 : if( FD_UNLIKELY( err && errno != ENOENT ) )
47 0 : FD_LOG_ERR(( "error checking if device `%s` is bonded, stat(%s) failed (%i-%s)",
48 0 : device, path, errno, fd_io_strerror( errno ) ));
49 0 : return !err;
50 0 : }
51 :
52 : static void
53 : device_read_slaves( char const * device,
54 0 : char output[ 4096 ] ) {
55 0 : char path[ PATH_MAX ];
56 0 : FD_TEST( fd_cstr_printf_check( path, PATH_MAX, NULL, "/sys/class/net/%s/bonding/slaves", device ) );
57 :
58 0 : FILE * fp = fopen( path, "r" );
59 0 : if( FD_UNLIKELY( !fp ) )
60 0 : FD_LOG_ERR(( "error configuring network device, fopen(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
61 0 : if( FD_UNLIKELY( !fgets( output, 4096, fp ) ) )
62 0 : FD_LOG_ERR(( "error configuring network device, fgets(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
63 0 : if( FD_UNLIKELY( feof( fp ) ) ) FD_LOG_ERR(( "error configuring network device, fgets(%s) failed (EOF)", path ));
64 0 : if( FD_UNLIKELY( ferror( fp ) ) ) FD_LOG_ERR(( "error configuring network device, fgets(%s) failed (error)", path ));
65 0 : if( FD_UNLIKELY( strlen( output ) == 4095 ) ) FD_LOG_ERR(( "line too long in `%s`", path ));
66 0 : if( FD_UNLIKELY( strlen( output ) == 0 ) ) FD_LOG_ERR(( "line empty in `%s`", path ));
67 0 : if( FD_UNLIKELY( fclose( fp ) ) )
68 0 : FD_LOG_ERR(( "error configuring network device, fclose(%s) failed (%i-%s)", path, errno, fd_io_strerror( errno ) ));
69 0 : output[ strlen( output ) - 1 ] = '\0';
70 0 : }
71 :
72 : static void
73 0 : init_device( char const * device ) {
74 0 : fd_ethtool_ioctl_t ioc;
75 0 : if( FD_UNLIKELY( &ioc != fd_ethtool_ioctl_init( &ioc, device ) ) )
76 0 : FD_LOG_ERR(( "error configuring network device (%s), unable to init ethtool ioctl", device ));
77 :
78 : /* turn off generic-receive-offload, which is entirely incompatible with
79 : * AF_XDP and QUIC
80 : * It results in multiple UDP payloads being merged into a single UDP packet,
81 : * with IP and UDP headers rewritten, combining the lengths and updating the
82 : * checksums. QUIC short packets cannot be processed reliably in this case. */
83 0 : FD_TEST( 0==fd_ethtool_ioctl_feature_gro_set( &ioc, 0 ) );
84 :
85 : /* turn off tx-gre-segmentation and tx-gre-csum-segmentation. When
86 : enabled, some packets sent via normal sockets can be corrupted
87 : while XDP is in use on the same system. */
88 0 : FD_TEST( 0==fd_ethtool_ioctl_feature_set( &ioc, FD_ETHTOOL_FEATURE_TXGRESEG, 0 ) );
89 0 : FD_TEST( 0==fd_ethtool_ioctl_feature_set( &ioc, FD_ETHTOOL_FEATURE_TXGRECSUMSEG, 0 ) );
90 :
91 0 : fd_ethtool_ioctl_fini( &ioc );
92 0 : }
93 :
94 : static void
95 0 : init( fd_config_t const * config ) {
96 0 : if( FD_UNLIKELY( device_is_bonded( config->net.interface ) ) ) {
97 : /* if using a bonded device, we need to disable gro on the
98 : underlying devices.
99 :
100 : we don't need to disable gro on the bonded device, as the packets are
101 : redirected by XDP before any of the kernel bonding logic */
102 0 : char line[ 4096 ];
103 0 : device_read_slaves( config->net.interface, line );
104 0 : char * saveptr;
105 0 : for( char * token=strtok_r( line , " \t", &saveptr ); token!=NULL; token=strtok_r( NULL, " \t", &saveptr ) ) {
106 0 : init_device( token );
107 0 : }
108 0 : } else {
109 0 : init_device( config->net.interface );
110 0 : }
111 0 : init_device( "lo" );
112 0 : }
113 :
114 : static configure_result_t
115 0 : check_device( char const * device ) {
116 0 : fd_ethtool_ioctl_t ioc;
117 0 : if( FD_UNLIKELY( &ioc != fd_ethtool_ioctl_init( &ioc, device ) ) )
118 0 : FD_LOG_ERR(( "error configuring network device (%s), unable to init ethtool ioctl", device ));
119 :
120 0 : int gro_active;
121 0 : FD_TEST( 0==fd_ethtool_ioctl_feature_gro_test( &ioc, &gro_active ) );
122 :
123 0 : int greseg_active;
124 0 : int grecsumseg_active;
125 0 : FD_TEST( 0==fd_ethtool_ioctl_feature_test( &ioc, FD_ETHTOOL_FEATURE_TXGRESEG, &greseg_active ) );
126 0 : FD_TEST( 0==fd_ethtool_ioctl_feature_test( &ioc, FD_ETHTOOL_FEATURE_TXGRECSUMSEG, &grecsumseg_active ) );
127 :
128 0 : fd_ethtool_ioctl_fini( &ioc );
129 :
130 0 : if( FD_UNLIKELY( gro_active ) )
131 0 : NOT_CONFIGURED( "device `%s` has generic-receive-offload enabled. Should be disabled", device );
132 :
133 0 : if( FD_UNLIKELY( greseg_active ) )
134 0 : NOT_CONFIGURED( "device `%s` has tx-gre-segmentation enabled. Should be disabled", device );
135 0 : if( FD_UNLIKELY( grecsumseg_active ) )
136 0 : NOT_CONFIGURED( "device `%s` has tx-gre-csum-segmentation enabled. Should be disabled", device );
137 :
138 0 : CONFIGURE_OK();
139 0 : }
140 :
141 : static configure_result_t
142 0 : check( fd_config_t const * config ) {
143 0 : if( FD_UNLIKELY( device_is_bonded( config->net.interface ) ) ) {
144 0 : char line[ 4096 ];
145 0 : device_read_slaves( config->net.interface, line );
146 0 : char * saveptr;
147 0 : for( char * token=strtok_r( line, " \t", &saveptr ); token!=NULL; token=strtok_r( NULL, " \t", &saveptr ) ) {
148 0 : CHECK( check_device( token ) );
149 0 : }
150 0 : } else {
151 0 : CHECK( check_device( config->net.interface ) );
152 0 : }
153 0 : CHECK( check_device( "lo" ) );
154 :
155 0 : CONFIGURE_OK();
156 0 : }
157 :
158 : configure_stage_t fd_cfg_stage_ethtool_offloads = {
159 : .name = NAME,
160 : .always_recreate = 0,
161 : .enabled = enabled,
162 : .init_perm = init_perm,
163 : .fini_perm = NULL,
164 : .init = init,
165 : .fini = NULL,
166 : .check = check,
167 : };
168 :
169 : #undef NAME
|