Reputation: 22366
I'm trying to use the Ethtool ioctl API to retrieve linkspeed data from my NICs, but I just just get zeroes back in the ethtool_link_settings
instance. Using the ethtool
command line tool returns the expected values, and my NIC driver supports the newer ETHTOOL_GLINKSETTINGS
API.
#include <iostream>
#include <cstring>
#include <linux/ethtool.h>
#include <linux/sockios.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <unistd.h>
int main()
{
auto ifn = if_nameindex();
auto fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
for (auto i = ifn; i->if_name; ++i) {
// Skip the loopback
if (i->if_index == 1) {
continue;
}
std::cout << "Testing: " << i->if_name << std::endl;
auto ifr = ifreq{};
std::strncpy(ifr.ifr_name, i->if_name, IF_NAMESIZE);
auto msg = ethtool_link_settings{};
msg.cmd = ETHTOOL_GLINKSETTINGS;
ifr.ifr_data = reinterpret_cast<char*>(&msg);
if (ioctl(fd, SIOCETHTOOL, &ifr) == -1) {
std::cerr << "ioctl fail: " << strerror(errno) << std::endl;
}
std::cout << "\tSpeed: " << msg.speed
<< "\n\tDuplex: " << static_cast<int>(msg.duplex)
<< "\n\tPort: " << static_cast<int>(msg.port)
<< std::endl;
}
close(fd);
if_freenameindex(ifn);
return EXIT_SUCCESS;
}
Results in:
Testing: enp0s3
Speed: 0
Duplex: 0
Port: 0
Testing: enp0s8
Speed: 0
Duplex: 0
Port: 0
Testing: enp0s9
Speed: 0
Duplex: 0
Port: 0
Testing: enp0s10
Speed: 0
Duplex: 0
Port: 0
I'm sure I'm doing something stupid, but I can't see it.
Upvotes: 1
Views: 1570
Reputation: 229284
Buried in the comments of the link_mode_masks_nwords
field of struct ethtool_link_settings
in /usr/include/linux/ethtool.h
are some admirable cryptic comments that ETHTOOL_GLINKSETTINGS does a bit of handshaking, so you'll need to call it more than once.
I've adjusted your code by using the code of the ethtool command to perform the ETHTOOL_GLINKSETTINGS command:
#include <iostream>
#include <cstring>
#include <linux/ethtool.h>
#include <linux/sockios.h>
#include <linux/netlink.h>
#include <netinet/in.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <unistd.h>
int main()
{
auto ifn = if_nameindex();
auto fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
for (auto i = ifn; i->if_name; ++i) {
struct {
struct ethtool_link_settings req;
__u32 link_mode_data[3 * 127];
} ecmd;
// Skip the loopback
if (i->if_index == 1) {
continue;
}
std::cout << "Testing: " << i->if_name << std::endl;
auto ifr = ifreq{};
std::strncpy(ifr.ifr_name, i->if_name, IF_NAMESIZE);
ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
ifr.ifr_data = reinterpret_cast<char*>(&ecmd);
if (ioctl(fd, SIOCETHTOOL, &ifr) == -1) {
std::cerr << "ioctl fail: " << strerror(errno) << std::endl;
return 1;
}
if (ecmd.req.link_mode_masks_nwords >= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
return 1;
ecmd.req.link_mode_masks_nwords = -ecmd.req.link_mode_masks_nwords;
if (ioctl(fd, SIOCETHTOOL, &ifr) == -1) {
std::cerr << "ioctl fail: " << strerror(errno) << std::endl;
return 1;
}
std::cout << "\tSpeed: " << ecmd.req.speed
<< "\n\tDuplex: " << static_cast<int>(ecmd.req.duplex)
<< "\n\tPort: " << static_cast<int>(ecmd.req.port)
<< std::endl;
}
close(fd);
if_freenameindex(ifn);
return EXIT_SUCCESS;
}
Upvotes: 1