Skip to content

Instantly share code, notes, and snippets.

@kmullin
Last active September 12, 2019 15:23
Show Gist options
  • Save kmullin/8310441 to your computer and use it in GitHub Desktop.
Save kmullin/8310441 to your computer and use it in GitHub Desktop.
determine speed of network interfaces in linux using ioctl, and getifaddrs(3) will detect bonding interfaces, and sum speeds of slave devices. purpose for writing was to use with puppet and external facts
/*
* most of the code shamelessly stolen from:
* http://stackoverflow.com/a/2876605
*
* more code stolen from getifaddrs(3) manpage
*
* output is: interface=speed
* written by Kevin Mullin 01/07/14
*
*/
#include <stdio.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <linux/sockios.h>
#include <linux/if.h>
#include <linux/ethtool.h>
#include <string.h>
#include <stdlib.h>
#include <ifaddrs.h>
#include <getopt.h>
#include <libgen.h>
void version_info()
{
printf("Version: %s\n"
"Github source: https://gist.github.com/kmullin/8310441\n",
__TIMESTAMP__);
}
/*
* get_interface_speed:
* takes: socket file descriptor, and interface name
* returns: speed of interface
*
* uses ioctl and ETHTOOL API (in linux) to return driver level
* speed information for a specified device name
*
*/
int get_interface_speed(int sock, char *ifname)
{
struct ifreq ifr;
struct ethtool_cmd edata;
int rc;
// string copy first argument into struct
strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
ifr.ifr_data = &edata;
// set some global options from ethtool API
edata.cmd = ETHTOOL_GSET;
// issue ioctl
rc = ioctl(sock, SIOCETHTOOL, &ifr);
if (rc < 0) {
perror("ioctl");
// lets not error out here
// make sure to zero out speed
return 0;
}
return edata.speed;
}
/*
* get_bond_slave_speed:
* takes: socket file descriptor, and bond interface_name
* returns: sum of speeds of slave interfaces
*
* will read /sys/class/net/IFACE/bonding/slaves to determine slave devices
* will read speed from slave devices, and return sum of speeds of all slave interfaces
*
*/
int get_bond_slave_speed(int sock, char *ifname)
{
int total_bw = 0;
char path[40];
char line[50];
char *interface;
FILE *fp;
// hackish, how do we know the length of string buffer
strcpy(path, "/sys/class/net/");
strcat(path, ifname);
strcat(path, "/bonding/slaves");
fp = fopen(path, "r");
if (fp == NULL) {
perror("Can't open sys file for bond interface");
exit(1);
}
fgets(line, 50, fp);
interface = strtok(line, " \n");
while (interface != NULL) {
total_bw += get_interface_speed(sock, interface);
interface = strtok(NULL, " \n");
}
return total_bw;
}
int main (int argc, char **argv)
{
int sock, tmp, c;
struct ifaddrs *ifaddr, *ifa;
int max_speed = 0;
int verbose = 0;
int do_usage = 0;
struct option longopts[] = {
{ "verbose", no_argument, &verbose, 1 },
{ "version", no_argument, 0, 'V' },
{ "help", no_argument, &do_usage, 1 },
{ 0, 0, 0, 0}
};
while ((c = getopt_long(argc, argv, "Vv", longopts, NULL)) != -1)
switch (c) {
case 'V':
version_info();
exit(1);
case 'v':
verbose = 1;
break;
default:
break;
}
if (do_usage == 1) {
printf("Print speed of all ethernet devices\n"
"Usage %s\n"
"\n"
"\t--help\t\tPrint Usage\n"
"\t-V|--version\tPrint Version info\n"
"\t-v|--verbose\tBe verbose\n"
"\n",
basename(argv[0]));
exit(1);
}
// setup socket for ioctl
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock < 0) {
perror("socket");
exit(1);
}
// attempt to fill struct with ifaddrs
if (getifaddrs(&ifaddr) == -1) {
perror("getifaddrs");
exit(1);
}
// code stolen from getifaddrs(3) manpage
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
// walk interfaces
if (ifa->ifa_addr == NULL)
continue;
// get only PACKET interfaces
if (ifa->ifa_addr->sa_family == AF_PACKET) {
// if loopback, or not running ignore
if ((strcmp("lo", ifa->ifa_name) == 0) ||
!(ifa->ifa_flags & (IFF_RUNNING)))
continue;
if (strncmp(ifa->ifa_name, "bond", 4) != 0) {
// if not a bond interface, go ahead print speed
tmp = get_interface_speed(sock, ifa->ifa_name);
} else {
// if it is a bond interface, go get aggregate of sub-interface speeds
tmp = get_bond_slave_speed(sock, ifa->ifa_name);
}
// keep track of the greatest speed
if (tmp > max_speed)
max_speed = tmp;
printf("%s_speed=%d\n", ifa->ifa_name, tmp);
}
}
printf("max_nic_speed=%d\n", max_speed);
// no clue what this does, apparently free pointers
freeifaddrs(ifaddr);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment