Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get ethtool settings?

Tags:

c++

c

linux

ioctl

Please, help me get ethtool settings (speed, duplex, autoneg).

If I use ETHTOOL_GSET, I get ethtool settings. But in ethtool.h written to use ETHTOOL_GLINKSETTINGS instead of ETHTOOL_GSET. I don't know how to use ETHTOOL_GLINKSETTINGS.

ETHTOOL_GSET

#include <stdio.h>
#include <string.h>
#include <net/if.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/ethtool.h>
#include <linux/sockios.h>

int main()
{
    int s; // socket
    int r; // result

    struct ifreq ifReq;
    strncpy(ifReq.ifr_name, "enp3s0", sizeof(ifReq.ifr_name));

    struct ethtool_cmd ethtoolCmd;
    ethtoolCmd.cmd = ETHTOOL_GSET;
    ifReq.ifr_data = &ethtoolCmd;

    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s != -1)
    {
        r = ioctl(s, SIOCETHTOOL, &ifReq);
        if (s != -1)
        {
            printf("%s | ethtool_cmd.speed = %i \n", ifReq.ifr_name, ethtoolCmd.speed);
            printf("%s | ethtool_cmd.duplex = %i \n", ifReq.ifr_name, ethtoolCmd.duplex);
            printf("%s | ethtool_cmd.autoneg = %i \n", ifReq.ifr_name, ethtoolCmd.autoneg);
        }
        else
            printf("Error #r");

        close(s);
    }
    else
        printf("Error #s");

    return 0;
}

Result:

enp3s0 | ethtool_cmd.speed = 1000 
enp3s0 | ethtool_cmd.duplex = 1
enp3s0 | ethtool_cmd.autoneg = 1

ETHTOOL_GLINKSETTINGS

#include <stdio.h>
#include <string.h>
#include <net/if.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/ethtool.h>
#include <linux/sockios.h>

int main()
{
    int s; // socket
    int r; // result

    struct ifreq ifReq;
    strncpy(ifReq.ifr_name, "enp3s0", sizeof(ifReq.ifr_name));

    struct ethtool_link_settings ethtoolLinkSettings;
    ethtoolLinkSettings.cmd = ETHTOOL_GLINKSETTINGS;
    ifReq.ifr_data = &ethtoolLinkSettings;

    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s != -1)
    {
        r = ioctl(s, SIOCETHTOOL, &ifReq);
        if (s != -1)
        {
            printf("%s | ethtool_link_settings.speed = %i \n", ifReq.ifr_name, ethtoolLinkSettings.speed);
            printf("%s | ethtool_link_settings.duplex = %i \n", ifReq.ifr_name, ethtoolLinkSettings.duplex);
            printf("%s | ethtool_link_settings.autoneg = %i \n", ifReq.ifr_name, ethtoolLinkSettings.autoneg);
        }
        else
            printf("Error #r");

        close(s);
    }
    else
        printf("Error #s");

    return 0;
}

Result:

enp3s0 | ethtool_link_settings.speed = 0 
enp3s0 | ethtool_link_settings.duplex = 45
enp3s0 | ethtool_link_settings.autoneg = 0

Why ETHTOOL_GLINKSETTINGS return incorrect values? What is the problem?

like image 954
user7461659 Avatar asked Oct 19 '25 12:10

user7461659


2 Answers

Definitely this code is a problem

r = ioctl(s, SIOCETHTOOL, &ifReq);
    if (s != -1)

how ever fixing this doesn't resolve the issue and the interface properties cannot be queried using ETHTOOL_GLINKSETTINGS. A peek into the header file shows this.


from /usr/include/linux/ethtool.h 

1532 /**                                                                             
1533  * struct ethtool_link_settings - link control and status                       
1534  *                                                                              
1535  * IMPORTANT, Backward compatibility notice: When implementing new              
1536  *  user-space tools, please first try %ETHTOOL_GLINKSETTINGS, and              
1537  *  if it succeeds use %ETHTOOL_SLINKSETTINGS to change link                    
1538  *  settings; do not use %ETHTOOL_SSET if %ETHTOOL_GLINKSETTINGS                
1539  *  succeeded: stick to %ETHTOOL_GLINKSETTINGS/%SLINKSETTINGS in                
1540  *  that case.  Conversely, if %ETHTOOL_GLINKSETTINGS fails, use                
1541  *  %ETHTOOL_GSET to query and %ETHTOOL_SSET to change link                     
1542  *  settings; do not use %ETHTOOL_SLINKSETTINGS if                              
1543  *  %ETHTOOL_GLINKSETTINGS failed: stick to                                     
1544  *  %ETHTOOL_GSET/%ETHTOOL_SSET in that case.       

I do see the same behavior as reported and observe the correct values being reported using ETHTOOL_GSET

with ETHTOOL_GSET

~/working/exp$./ethtool-exp ens33
ens33 | ethtool_cmd.speed = 1000 
ens33 | ethtool_cmd.duplex = 1 
ens33 | ethtool_cmd.advertising= 239 
ens33 | ethtool_cmd.autoneg = 1 
ens33 | ethtool_cmd.port= 0 
ens33 | ethtool_cmd.phy_address= 0 
ens33 | ethtool_cmd.transceiver= 0 
ens33 | ethtool_cmd.maxrxpkt= 0 
ens33 | ethtool_cmd.maxtxpkt= 0 

with ETHTOOL_GLINKSETTINGS

~/working/exp$./ethtool-glink ens33
ens33 | ethtool_link_settings.speed = 0 
ens33 | ethtool_link_settings.duplex = 0 
ens33 | ethtool_link_settings.autoneg = 0 

like image 84
psglinux Avatar answered Oct 21 '25 02:10

psglinux


As you can see in the function do_ioctl_glinksettings() in ethtool.c, you should reserve some space behind your ethtoolLinkSettings buffer for the variable sized members map_supported, map_advertising, and map_lp_advertising (only indirectly accessible via link_mode_masks).

As a result of the first call of ioctl() you will get the real size of these members in link_mode_masks_nwords with a negative sign. Then set the link_mode_masks_nwords to this real size (non-negative) and call ioctl() again. Then you will get real data.

eg. (not tested)

int fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd > 0)
{
  struct ifreq ifr;
  memset(&ifr, 0, sizeof(ifr));
  strcpy(ifr.ifr_name, "enp3s0");

  char buffer[sizeof(struct ethtool_link_settings) + sizeof(__u32) * 3 * 128];
  struct ethtool_link_settings* ethtoolLinkSettings = (struct ethtool_link_settings*)buffer;
  memset(buffer, 0, sizeof(buffer));
  ethtoolLinkSettings->cmd = ETHTOOL_GLINKSETTINGS;
  ifr.ifr_data = (caddr_t)ethtoolLinkSettings;

  if (ioctl(fd, SIOCETHTOOL, &ifr) != -1)
  {
    if (ethtoolLinkSettings->link_mode_masks_nwords < 0)
    {
      ethtoolLinkSettings->cmd = ETHTOOL_GLINKSETTINGS;
      ethtoolLinkSettings->link_mode_masks_nwords = -ethtoolLinkSettings->link_mode_masks_nwords;

      if (ioctl(fd, SIOCETHTOOL, &ifr) != -1)
      {
        printf("%s | ethtool_link_settings.speed = %i \n", ifReq.ifr_name, ethtoolLinkSettings.speed);
        printf("%s | ethtool_link_settings.duplex = %i \n", ifReq.ifr_name, ethtoolLinkSettings.duplex);
        printf("%s | ethtool_link_settings.autoneg = %i \n", ifReq.ifr_name, ethtoolLinkSettings.autoneg);
      }
    }
  }
}
like image 34
some guy Avatar answered Oct 21 '25 03:10

some guy