Last active
September 12, 2019 15:23
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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