LCOV - code coverage report
Current view: top level - app/shared/commands/configure - fd_ethtool_ioctl.c (source / functions) Hit Total Coverage
Test: cov.lcov Lines: 0 324 0.0 %
Date: 2025-09-16 04:29:32 Functions: 0 14 0.0 %

          Line data    Source code
       1             : #include <errno.h>
       2             : #include <unistd.h>
       3             : #include <linux/ethtool.h>
       4             : #include <linux/sockios.h>
       5             : #include <net/if.h>
       6             : #include <sys/ioctl.h>
       7             : #include <sys/socket.h>
       8             : 
       9             : #include "fd_ethtool_ioctl.h"
      10             : #include "../../../../util/fd_util.h"
      11             : 
      12             : #define MAX_RXFH_KEY_SIZE   (1024)
      13           0 : #define MAX_FEATURES        (2048)
      14             : #define MAX_NTUPLE_RULES    (8192)
      15             : 
      16             : #define ETHTOOL_CMD_SIZE( base_t, data_t, data_len ) ( sizeof(base_t) + (sizeof(data_t)*(data_len)) )
      17             : 
      18             : static int
      19             : run_ioctl( fd_ethtool_ioctl_t * ioc,
      20             :            char const *         cmd,
      21             :            void *               data,
      22             :            int                  log,
      23           0 :            int                  log_notsupp ) {
      24           0 :   ioc->ifr.ifr_data = data;
      25           0 :   if( FD_UNLIKELY( ioctl( ioc->fd, SIOCETHTOOL, &ioc->ifr ) ) ) {
      26           0 :     if( (!!log) & ((errno!=EOPNOTSUPP) | (!!log_notsupp)) )
      27           0 :       FD_LOG_WARNING(( "error configuring network device (%s), ioctl(SIOCETHTOOL,%s) failed (%i-%s)",
      28           0 :                        ioc->ifr.ifr_name, cmd, errno, fd_io_strerror( errno ) ));
      29           0 :     return errno;
      30           0 :   }
      31           0 :   return 0;
      32           0 : }
      33             : 
      34             : #define TRY_RUN_IOCTL( ioc, cmd, data ) \
      35           0 :   do { int __ret__ = run_ioctl( (ioc), (cmd), (data), 1, 1 ); \
      36           0 :        if( FD_UNLIKELY( __ret__ != 0 ) ) { return __ret__; } } while(0)
      37             : 
      38             : fd_ethtool_ioctl_t *
      39             : fd_ethtool_ioctl_init( fd_ethtool_ioctl_t * ioc,
      40           0 :                        char const * device ) {
      41           0 :   if( FD_UNLIKELY( strlen( device ) >= IF_NAMESIZE ) ) {
      42           0 :     FD_LOG_WARNING(( "device name `%s` is too long", device ));
      43           0 :     return NULL;
      44           0 :   }
      45           0 :   if( FD_UNLIKELY( strlen( device ) == 0 ) ) {
      46           0 :     FD_LOG_WARNING(( "device name `%s` is empty", device ));
      47           0 :     return NULL;
      48           0 :   }
      49             : 
      50           0 :   ioc->fd = socket( AF_INET, SOCK_DGRAM, 0 );
      51           0 :   if( FD_UNLIKELY( ioc->fd < 0 ) ) {
      52           0 :     FD_LOG_WARNING(( "error configuring network device (%s), socket(AF_INET,SOCK_DGRAM,0) failed (%i-%s)",
      53           0 :                      device, errno, fd_io_strerror( errno ) ));
      54           0 :     return NULL;
      55           0 :   }
      56             : 
      57           0 :   fd_memset( &ioc->ifr, 0, sizeof(struct ifreq) );
      58           0 :   fd_cstr_fini( fd_cstr_append_cstr( fd_cstr_init( ioc->ifr.ifr_name ), device ) );
      59             : 
      60           0 :   return ioc;
      61           0 : }
      62             : 
      63             : void
      64           0 : fd_ethtool_ioctl_fini( fd_ethtool_ioctl_t * ioc ) {
      65           0 :   if( FD_UNLIKELY( close( ioc->fd ) ) ) {
      66           0 :     FD_LOG_WARNING(( "error configuring network device (%s), close() socket failed (%i-%s)",
      67           0 :                      ioc->ifr.ifr_name, errno, fd_io_strerror( errno ) ));
      68           0 :   }
      69           0 :   ioc->fd = -1;
      70           0 :   fd_memset( &ioc->ifr, 0, sizeof(struct ifreq) );
      71           0 : }
      72             : 
      73             : int
      74             : fd_ethtool_ioctl_channels_set_num( fd_ethtool_ioctl_t * ioc,
      75           0 :                                    uint                 num ) {
      76           0 :   struct ethtool_channels ech = { .cmd = ETHTOOL_GCHANNELS };
      77           0 :   int ret = run_ioctl( ioc, "ETHTOOL_GCHANNELS", &ech, 1, 0 );
      78           0 :   if( FD_UNLIKELY( ret!=0 ) ) {
      79           0 :     if( (ret==EOPNOTSUPP) & ((num==0) | (num==1))) return 0;
      80           0 :     return ret;
      81           0 :   }
      82             : 
      83           0 :   ech.cmd = ETHTOOL_SCHANNELS;
      84           0 :   if( num == 0 ) {
      85           0 :     uint max_queue_count = ech.max_combined ? ech.max_combined : ech.max_rx;
      86           0 :     num = fd_uint_min( max_queue_count, (uint)fd_shmem_cpu_cnt() );
      87           0 :   }
      88           0 :   if( ech.max_combined ) {
      89           0 :     ech.combined_count = num;
      90           0 :     ech.rx_count       = 0;
      91           0 :     ech.tx_count       = 0;
      92           0 :     FD_LOG_NOTICE(( "RUN: `ethtool --set-channels %s combined %u`", ioc->ifr.ifr_name, num ));
      93           0 :   } else {
      94           0 :     ech.combined_count = 0;
      95           0 :     ech.rx_count       = num;
      96           0 :     ech.tx_count       = num;
      97           0 :     FD_LOG_NOTICE(( "RUN: `ethtool --set-channels %s rx %u tx %u`", ioc->ifr.ifr_name, num, num ));
      98           0 :   }
      99           0 :   TRY_RUN_IOCTL( ioc, "ETHTOOL_SCHANNELS", &ech );
     100           0 :   return 0;
     101           0 : }
     102             : 
     103             : int
     104             : fd_ethtool_ioctl_channels_get_num( fd_ethtool_ioctl_t * ioc,
     105           0 :                                    fd_ethtool_ioctl_channels_t * channels ) {
     106           0 :   struct ethtool_channels ech = { .cmd = ETHTOOL_GCHANNELS };
     107           0 :   int ret = run_ioctl( ioc, "ETHTOOL_GCHANNELS", &ech, 1, 0 );
     108           0 :   if( FD_UNLIKELY( ret!=0 ) ) {
     109           0 :     if( FD_LIKELY( ret==EOPNOTSUPP ) ) {
     110             :       /* network device doesn't support getting number of channels, so
     111             :          it must always be 1 */
     112           0 :       channels->supported = 0;
     113           0 :       channels->current = 1;
     114           0 :       channels->max = 1;
     115           0 :       return 0;
     116           0 :     }
     117           0 :     return ret;
     118           0 :   }
     119           0 :   channels->supported = 1;
     120             : 
     121           0 :   if( FD_LIKELY( ech.combined_count ) ) {
     122           0 :     channels->current = ech.combined_count;
     123           0 :     channels->max = fd_uint_min( ech.max_combined, (uint)fd_shmem_cpu_cnt() );
     124           0 :     return 0;
     125           0 :   }
     126           0 :   if( ech.rx_count || ech.tx_count ) {
     127           0 :     if( FD_UNLIKELY( ech.rx_count != ech.tx_count ) )
     128           0 :       FD_LOG_WARNING(( "device `%s` has unbalanced channel count: (got %u rx, %u tx)",
     129           0 :                        ioc->ifr.ifr_name, ech.rx_count, ech.tx_count ));
     130           0 :     channels->current = ech.rx_count;
     131           0 :     channels->max = fd_uint_min( ech.max_rx, (uint)fd_shmem_cpu_cnt() );
     132           0 :     return 0;
     133           0 :   }
     134           0 :   return EINVAL;
     135           0 : }
     136             : 
     137             : int
     138           0 : fd_ethtool_ioctl_rxfh_set_default( fd_ethtool_ioctl_t * ioc ) {
     139           0 :   FD_LOG_NOTICE(( "RUN: `ethtool --set-rxfh-indir %s default`", ioc->ifr.ifr_name ));
     140           0 :   struct ethtool_rxfh_indir rxfh = {
     141           0 :     .cmd = ETHTOOL_SRXFHINDIR,
     142           0 :     .size = 0, /* default indirection table */
     143           0 :   };
     144           0 :   int ret = run_ioctl( ioc, "ETHTOOL_SRXFHINDIR", &rxfh, 1, 0 );
     145           0 :   if( FD_UNLIKELY( ret==EOPNOTSUPP ) ) return 0;
     146           0 :   return ret;
     147           0 : }
     148             : 
     149             : int
     150             : fd_ethtool_ioctl_rxfh_set_suffix( fd_ethtool_ioctl_t * ioc,
     151           0 :                                   uint                 start_idx ) {
     152             :   /* Get current channel count */
     153           0 :   struct ethtool_channels ech = { .cmd = ETHTOOL_GCHANNELS };
     154           0 :   TRY_RUN_IOCTL( ioc, "ETHTOOL_GCHANNELS", &ech );
     155           0 :   uint const num_channels = ech.combined_count ? ech.combined_count : ech.rx_count;
     156           0 :   if( FD_UNLIKELY( start_idx >= num_channels ))
     157           0 :     return EINVAL;
     158             : 
     159           0 :   union {
     160           0 :     struct ethtool_rxfh_indir m;
     161           0 :     uchar _[ ETHTOOL_CMD_SIZE( struct ethtool_rxfh_indir, uint, FD_ETHTOOL_MAX_RXFH_TABLE_CNT ) ];
     162           0 :   } rxfh = { 0 };
     163             : 
     164             :   /* Get count of rx indirection table */
     165           0 :   rxfh.m.cmd = ETHTOOL_GRXFHINDIR;
     166           0 :   rxfh.m.size = 0;
     167           0 :   TRY_RUN_IOCTL( ioc, "ETHTOOL_GRXFHINDIR", &rxfh );
     168           0 :   uint const table_ele_cnt = rxfh.m.size;
     169           0 :   if( FD_UNLIKELY( (table_ele_cnt == 0) | (table_ele_cnt > FD_ETHTOOL_MAX_RXFH_TABLE_CNT) ) )
     170           0 :     return EINVAL;
     171             : 
     172             :   /* Set table to round robin over all channels from [start_idx, num_channels) */
     173           0 :   FD_LOG_NOTICE(( "RUN: `ethtool --set-rxfh-indir %s start %u equal %u`",
     174           0 :                   ioc->ifr.ifr_name, start_idx, num_channels - start_idx ));
     175           0 :   rxfh.m.cmd = ETHTOOL_SRXFHINDIR;
     176           0 :   rxfh.m.size = table_ele_cnt;
     177           0 :   uint i = start_idx;
     178           0 :   for(uint j=0u; j<table_ele_cnt; j++) {
     179           0 :     rxfh.m.ring_index[ j ] = i++;
     180           0 :     if( i >= num_channels )
     181           0 :       i = start_idx;
     182           0 :   }
     183           0 :   TRY_RUN_IOCTL( ioc, "ETHTOOL_SRXFHINDIR", &rxfh );
     184             : 
     185           0 :   return 0;
     186           0 : }
     187             : 
     188             : int
     189             : fd_ethtool_ioctl_rxfh_get_table( fd_ethtool_ioctl_t * ioc,
     190             :                                  uint *               table,
     191           0 :                                  uint *               table_ele_cnt ) {
     192             :   /* Note: A simpler implementation of this would use ETHTOOL_GRXFHINDIR
     193             :      as we are only concerned with the indirection table and do not need
     194             :      the other information. However, it appears that the ice driver has
     195             :      a bugged implementation of this command. */
     196             : 
     197           0 :   union {
     198           0 :     struct ethtool_rxfh m;
     199           0 :     uchar _[ ETHTOOL_CMD_SIZE( struct ethtool_rxfh, uint, FD_ETHTOOL_MAX_RXFH_TABLE_CNT ) + MAX_RXFH_KEY_SIZE ];
     200           0 :   } rxfh = { 0 };
     201             : 
     202             :   /* First get the count of the indirection table and hash key */
     203           0 :   rxfh.m.cmd = ETHTOOL_GRSSH;
     204           0 :   int ret = run_ioctl( ioc, "ETHTOOL_GRSSH", &rxfh, 1, 0 );
     205           0 :   if( FD_UNLIKELY( ret!=0 ) ) {
     206           0 :     if( FD_LIKELY( ret==EOPNOTSUPP ) ) {
     207           0 :       *table_ele_cnt = 0;
     208           0 :       return 0;
     209           0 :     }
     210           0 :     return ret;
     211           0 :   }
     212           0 :   if( FD_UNLIKELY( (rxfh.m.indir_size == 0) | (rxfh.m.indir_size > FD_ETHTOOL_MAX_RXFH_TABLE_CNT) |
     213           0 :                    (rxfh.m.key_size   == 0) | (rxfh.m.key_size   > MAX_RXFH_KEY_SIZE) ) )
     214           0 :     return EINVAL;
     215           0 :   *table_ele_cnt = rxfh.m.indir_size;
     216             : 
     217             :   /* Now get the table contents itself. We also get the key bytes. */
     218           0 :   TRY_RUN_IOCTL( ioc, "ETHTOOL_GRSSH", &rxfh );
     219           0 :   fd_memcpy( table, rxfh.m.rss_config, *table_ele_cnt * sizeof(uint) );
     220           0 :   return 0;
     221           0 : }
     222             : 
     223             : static int
     224             : get_feature_idx( fd_ethtool_ioctl_t * ioc,
     225             :                  char const *         name,
     226           0 :                  uint *               feature_idx ) {
     227             :   /* Check size of features string set is not too large (prevent overflow) */
     228           0 :   union {
     229           0 :     struct ethtool_sset_info m;
     230           0 :     uchar _[ ETHTOOL_CMD_SIZE( struct ethtool_sset_info, uint, 1 ) ];
     231           0 :   } esi = { .m = {
     232           0 :     .cmd = ETHTOOL_GSSET_INFO,
     233           0 :     .sset_mask = fd_ulong_mask_bit( ETH_SS_FEATURES )
     234           0 :   } };
     235           0 :   TRY_RUN_IOCTL( ioc, "ETHTOOL_GSSET_INFO", &esi );
     236           0 :   if( FD_UNLIKELY( (esi.m.data[0] == 0) | (esi.m.data[0] > MAX_FEATURES) ) )
     237           0 :     return EINVAL;
     238             : 
     239             :   /* Get strings from features string set */
     240           0 :   union {
     241           0 :     struct ethtool_gstrings m;
     242           0 :     uchar _[ sizeof(struct ethtool_gstrings) + (MAX_FEATURES * ETH_GSTRING_LEN) ];
     243           0 :   } egs = { 0 };
     244           0 :   egs.m.cmd = ETHTOOL_GSTRINGS;
     245           0 :   egs.m.string_set = ETH_SS_FEATURES;
     246           0 :   TRY_RUN_IOCTL( ioc, "ETHTOOL_GSTRINGS", &egs );
     247             : 
     248             :   /* Find index of matching string from the set */
     249           0 :   for( uint i=0U; i<egs.m.len; i++) {
     250           0 :     uchar const * gstring = egs.m.data + (i * ETH_GSTRING_LEN);
     251           0 :     if( 0==strncmp( (char const *)gstring, name, ETH_GSTRING_LEN ) ) {
     252           0 :       *feature_idx = i;
     253           0 :       return 0;
     254           0 :     }
     255           0 :   }
     256           0 :   return EINVAL;
     257           0 : }
     258             : 
     259             : int
     260             : fd_ethtool_ioctl_feature_set( fd_ethtool_ioctl_t * ioc,
     261             :                               char const *         name,
     262           0 :                               int                  enabled ) {
     263           0 :   uint feature_idx;
     264           0 :   if( FD_UNLIKELY( 0!=get_feature_idx( ioc, name, &feature_idx ) ) )
     265           0 :     return EINVAL;
     266             : 
     267           0 :   FD_LOG_NOTICE(( "RUN: `ethtool --features %s %s %s`",
     268           0 :                   ioc->ifr.ifr_name, name, enabled ? "on" : "off" ));
     269           0 :   uint feature_block = feature_idx / 32u;
     270           0 :   uint feature_offset = feature_idx % 32u;
     271           0 :   union {
     272           0 :     struct ethtool_sfeatures m;
     273           0 :     uchar _[ ETHTOOL_CMD_SIZE( struct ethtool_sfeatures, struct ethtool_set_features_block, MAX_FEATURES / 32u ) ];
     274           0 :   } esf = { 0 };
     275           0 :   esf.m.cmd = ETHTOOL_SFEATURES;
     276           0 :   esf.m.size = feature_block + 1;
     277           0 :   esf.m.features[ feature_block ].valid = fd_uint_mask_bit( (int)feature_offset );
     278           0 :   esf.m.features[ feature_block ].requested = enabled ? fd_uint_mask_bit( (int)feature_offset ) : 0;
     279           0 :   TRY_RUN_IOCTL( ioc, "ETHTOOL_SFEATURES", &esf );
     280           0 :   return 0;
     281           0 : }
     282             : 
     283             : int
     284             : fd_ethtool_ioctl_feature_test( fd_ethtool_ioctl_t * ioc,
     285             :                                char const *         name,
     286           0 :                                int *                enabled ) {
     287           0 :   uint feature_idx;
     288           0 :   if( FD_UNLIKELY( 0!=get_feature_idx( ioc, name, &feature_idx ) ) )
     289           0 :     return EINVAL;
     290             : 
     291           0 :   uint feature_block = feature_idx / 32u;
     292           0 :   uint feature_offset = feature_idx % 32u;
     293           0 :   union {
     294           0 :     struct ethtool_gfeatures m;
     295           0 :     uchar _[ ETHTOOL_CMD_SIZE( struct ethtool_gfeatures, struct ethtool_get_features_block, MAX_FEATURES / 32u ) ];
     296           0 :   } egf = { 0 };
     297           0 :   egf.m.cmd = ETHTOOL_GFEATURES;
     298           0 :   egf.m.size = MAX_FEATURES / 32u;
     299           0 :   TRY_RUN_IOCTL( ioc, "ETHTOOL_GFEATURES", &egf );
     300             : 
     301           0 :   *enabled = !!(egf.m.features[ feature_block ].active & fd_uint_mask_bit( (int)feature_offset ));
     302           0 :   return 0;
     303           0 : }
     304             : 
     305             : int
     306           0 : fd_ethtool_ioctl_ntuple_clear( fd_ethtool_ioctl_t * ioc ) {
     307           0 :   union {
     308           0 :     struct ethtool_rxnfc m;
     309           0 :     uchar _[ ETHTOOL_CMD_SIZE( struct ethtool_rxnfc, uint, MAX_NTUPLE_RULES ) ];
     310           0 :   } efc = { 0 };
     311             : 
     312             :   /* Get count of currently defined rules, return if none exist */
     313           0 :   efc.m.cmd = ETHTOOL_GRXCLSRLCNT;
     314           0 :   int ret = run_ioctl( ioc, "ETHTOOL_GRXCLSRLCNT", &efc, 1, 0 );
     315           0 :   if( FD_UNLIKELY( ret!=0 ) ) {
     316           0 :     if( ret==EOPNOTSUPP ) return 0;
     317           0 :     return ret;
     318           0 :   }
     319           0 :   uint const rule_cnt = efc.m.rule_cnt;
     320           0 :   if( FD_UNLIKELY( rule_cnt > MAX_NTUPLE_RULES ) )
     321           0 :     return EINVAL;
     322           0 :   if( rule_cnt == 0 )
     323           0 :     return 0;
     324             : 
     325             :   /* Get location indices of all rules */
     326           0 :   efc.m.cmd = ETHTOOL_GRXCLSRLALL;
     327           0 :   TRY_RUN_IOCTL( ioc, "ETHTOOL_GRXCLSRLALL", &efc );
     328             : 
     329             :   /* Delete all rules */
     330           0 :   for( uint i=0u; i<rule_cnt; i++) {
     331           0 :     FD_LOG_NOTICE(( "RUN: `ethtool --config-ntuple %s delete %u`", ioc->ifr.ifr_name, efc.m.rule_locs[ i ] ));
     332           0 :     struct ethtool_rxnfc del = {
     333           0 :       .cmd = ETHTOOL_SRXCLSRLDEL,
     334           0 :       .fs = { .location = efc.m.rule_locs[ i ] }
     335           0 :     };
     336           0 :     TRY_RUN_IOCTL( ioc, "ETHTOOL_SRXCLSRLDEL", &del );
     337           0 :   }
     338             : 
     339           0 :   return 0;
     340           0 : }
     341             : 
     342             : int
     343             : fd_ethtool_ioctl_ntuple_set_udp_dport( fd_ethtool_ioctl_t * ioc,
     344             :                                        uint                 rule_idx,
     345             :                                        ushort               dport,
     346           0 :                                        uint                 queue_idx ) {
     347             :   /* Note: Some drivers do not support RX_CLS_LOC_ANY (e.g. mlx5), and
     348             :      some drivers only support it (e.g. bnxt). So first we try with
     349             :      the explicit rule index and then again with the any location if
     350             :      the former failed. */
     351           0 :   FD_LOG_NOTICE(( "RUN: `ethtool --config-ntuple %s flow-type udp4 dst-port %hu queue %u`",
     352           0 :                   ioc->ifr.ifr_name, dport, queue_idx ));
     353           0 :   struct ethtool_rxnfc efc = {
     354           0 :     .cmd = ETHTOOL_SRXCLSRLINS,
     355           0 :     .fs = {
     356           0 :       .flow_type = UDP_V4_FLOW,
     357           0 :       .h_u = { .udp_ip4_spec = { .pdst = fd_ushort_bswap( dport ) } },
     358           0 :       .m_u = { .udp_ip4_spec = { .pdst = 0xFFFF } },
     359           0 :       .ring_cookie = queue_idx,
     360           0 :       .location = rule_idx
     361           0 :     }
     362           0 :   };
     363           0 :   if( FD_LIKELY( 0==run_ioctl( ioc, "ETHTOOL_SRXCLSRLINS", &efc, 0, 0 ) ) )
     364           0 :     return 0;
     365           0 :   efc.fs.location = RX_CLS_LOC_ANY;
     366           0 :   TRY_RUN_IOCTL( ioc, "ETHTOOL_SRXCLSRLINS", &efc );
     367           0 :   return 0;
     368           0 : }
     369             : 
     370             : int
     371             : fd_ethtool_ioctl_ntuple_validate_udp_dport( fd_ethtool_ioctl_t * ioc,
     372             :                                             ushort *             dports,
     373             :                                             uint                 num_dports,
     374             :                                             uint                 queue_idx,
     375           0 :                                             int *                valid ) {
     376           0 :   union {
     377           0 :     struct ethtool_rxnfc m;
     378           0 :     uchar _[ ETHTOOL_CMD_SIZE( struct ethtool_rxnfc, uint, MAX_NTUPLE_RULES ) ];
     379           0 :   } efc = { 0 };
     380             : 
     381             :   /* Get count of currently defined rules */
     382           0 :   efc.m.cmd = ETHTOOL_GRXCLSRLCNT;
     383           0 :   int ret = run_ioctl( ioc, "ETHTOOL_GRXCLSRLCNT", &efc, 1, 0 );
     384           0 :   if( FD_UNLIKELY( ret!=0 ) ) {
     385           0 :     if( FD_LIKELY( ret==EOPNOTSUPP ) ) {
     386           0 :       *valid = (num_dports == 0);
     387           0 :       return 0;
     388           0 :     }
     389           0 :     return ret;
     390           0 :   }
     391           0 :   uint const rule_cnt = efc.m.rule_cnt;
     392           0 :   if( FD_UNLIKELY( rule_cnt > MAX_NTUPLE_RULES ) )
     393           0 :     return EINVAL;
     394           0 :   if( rule_cnt == 0 ) {
     395           0 :     *valid = (num_dports == 0);
     396           0 :     return 0;
     397           0 :   }
     398           0 :   if( num_dports == 0 ) {
     399           0 :     *valid = 0;
     400           0 :     return 0;
     401           0 :   }
     402             : 
     403             :   /* Get location indices of all rules */
     404           0 :   efc.m.cmd = ETHTOOL_GRXCLSRLALL;
     405           0 :   efc.m.rule_cnt = rule_cnt;
     406           0 :   TRY_RUN_IOCTL( ioc, "ETHTOOL_GRXCLSRLALL", &efc );
     407             : 
     408             :   /* Loop over all rules, returning early if any are invalid */
     409           0 :   static const union ethtool_flow_union EXPECTED_MASK = { .udp_ip4_spec = { .pdst = 0xFFFF } };
     410           0 :   static const struct ethtool_flow_ext EXPECTED_EXT_MASK = { 0 };
     411           0 :   for( uint i=0u; i<efc.m.rule_cnt; i++) {
     412           0 :     struct ethtool_rxnfc get = {
     413           0 :       .cmd = ETHTOOL_GRXCLSRULE,
     414           0 :       .fs = { .location = efc.m.rule_locs[ i ] }
     415           0 :     };
     416           0 :     TRY_RUN_IOCTL( ioc, "ETHTOOL_GRXCLSRULE", &get );
     417           0 :     if( FD_UNLIKELY( ((get.fs.flow_type != UDP_V4_FLOW) | (get.fs.ring_cookie != queue_idx)) ||
     418           0 :                      0!=memcmp( &get.fs.m_u, &EXPECTED_MASK, sizeof(EXPECTED_MASK) ) ||
     419           0 :                      0!=memcmp( &get.fs.m_ext, &EXPECTED_EXT_MASK, sizeof(EXPECTED_EXT_MASK)) ) ) {
     420           0 :       *valid = 0;
     421           0 :       return 0;
     422           0 :     }
     423             :     /* This is a valid udp rule, find the expected port(s) it matches or return error */
     424           0 :     int found = 0;
     425           0 :     for( uint j=0u; j<num_dports; j++) {
     426           0 :       if( dports[ j ] == fd_ushort_bswap( get.fs.h_u.udp_ip4_spec.pdst ) ) {
     427           0 :         dports[ j ] = 0u;
     428           0 :         found = 1;
     429           0 :       }
     430           0 :     }
     431           0 :     if( !found ) {
     432           0 :       *valid = 0;
     433           0 :       return 0;
     434           0 :     }
     435           0 :   }
     436             : 
     437             :   /* All rules are valid and matched expected ports. Lastly, check that
     438             :      no expected ports were missing */
     439           0 :   *valid = 1;
     440           0 :   for( uint i=0u; i<num_dports; i++)
     441           0 :     if( dports[ i ] != 0 )
     442           0 :       *valid = 0;
     443           0 :   return 0;
     444           0 : }

Generated by: LCOV version 1.14