8

I want to get the following details for all the NICs attached to my computer:

1) Interface name (eg. eth0)

2) Interface Number (like in Windows) if such a thing exists in Linux

3) NIC bandwidth capacity and mode (eg. 1Gb/s full duplex)

Bruce
  • 33,927
  • 76
  • 174
  • 262
  • 3
    [using C code to get same info as ifconfig](http://stackoverflow.com/questions/4951257/using-c-code-to-get-same-info-as-ifconfig), also chekcout [netdevice](http://linux.about.com/library/cmd/blcmdl7_netdevice.htm) – Joe Jan 10 '13 at 18:07
  • @Joe: It doesn't tell you 2 and 3. – Bruce Jan 10 '13 at 18:11
  • Its a start until you get an answer. – Joe Jan 10 '13 at 18:13

4 Answers4

20

You can use getifaddrs()/freeifaddrs() to obtain a linked list of all interfaces, then ioctl(fd, SIOCGIFINDEX, struct ifreq *) to obtain the interface index for each. Since the interfaces are consecutive and always listed (regardless of whether or they are up (active) or not), I choose to enumerate them with a loop using ioctl(fd, SIOCGIFNAME, struct ifreq *) instead. In all cases fd is an AF_INET socket.

To obtain the duplex and speed of the interface, you need to use the ioctl(fd, SIOCETHTOOL, struct ifreq *) with the ifr_data pointing to a struct ethtool_cmd having cmd = ETHTOOL_GSET.

The ioctls should return -1 if they fail, and a nonnegative value (zero, I believe) if success.

Here is an example program:

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

struct interface {
    int     index;
    int     flags;      /* IFF_UP etc. */
    long    speed;      /* Mbps; -1 is unknown */
    int     duplex;     /* DUPLEX_FULL, DUPLEX_HALF, or unknown */
    char    name[IF_NAMESIZE + 1];
};

static int get_interface_common(const int fd, struct ifreq *const ifr, struct interface *const info)
{
    struct ethtool_cmd  cmd;
    int                 result;

    /* Interface flags. */
    if (ioctl(fd, SIOCGIFFLAGS, ifr) == -1)
        info->flags = 0;
    else
        info->flags = ifr->ifr_flags;

    ifr->ifr_data = (void *)&cmd;
    cmd.cmd = ETHTOOL_GSET; /* "Get settings" */
    if (ioctl(fd, SIOCETHTOOL, ifr) == -1) {
        /* Unknown */
        info->speed = -1L;
        info->duplex = DUPLEX_UNKNOWN;
    } else {
        info->speed = ethtool_cmd_speed(&cmd);
        info->duplex = cmd.duplex;
    }

    do {
        result = close(fd);
    } while (result == -1 && errno == EINTR);
    if (result == -1)
        return errno;

    return 0;
}

int get_interface_by_index(const int index, struct interface *const info)
{
    int             socketfd, result;
    struct ifreq    ifr;

    if (index < 1 || !info)
        return errno = EINVAL;

    socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    if (socketfd == -1)
        return errno;

    ifr.ifr_ifindex = index;
    if (ioctl(socketfd, SIOCGIFNAME, &ifr) == -1) {
        do {
            result = close(socketfd);
        } while (result == -1 && errno == EINTR);
        return errno = ENOENT;
    }

    info->index = index;
    strncpy(info->name, ifr.ifr_name, IF_NAMESIZE);
    info->name[IF_NAMESIZE] = '\0';

    return get_interface_common(socketfd, &ifr, info);
}

int get_interface_by_name(const char *const name, struct interface *const info)
{
    int             socketfd, result;
    struct ifreq    ifr;

    if (!name || !*name || !info)
        return errno = EINVAL;

    socketfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
    if (socketfd == -1)
        return errno;

    strncpy(ifr.ifr_name, name, IF_NAMESIZE);
    if (ioctl(socketfd, SIOCGIFINDEX, &ifr) == -1) {
        do {
            result = close(socketfd);
        } while (result == -1 && errno == EINTR);
        return errno = ENOENT;
    }

    info->index = ifr.ifr_ifindex;
    strncpy(info->name, name, IF_NAMESIZE);
    info->name[IF_NAMESIZE] = '\0';

    return get_interface_common(socketfd, &ifr, info);
}

int main(int argc, char *argv[])
{
    struct interface    iface;
    int                 arg;
    int                 status = 0;

    if (argc > 1 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s { -h | --help }\n", argv[0]);
        fprintf(stderr, "       %s\n", argv[0]);
        fprintf(stderr, "       %s INTERFACE ...\n", argv[0]);
        fprintf(stderr, "\n");
        return 1;
    }

    if (argc > 1) {
        for (arg = 1; arg < argc; arg++) {
            if (get_interface_by_name(argv[arg], &iface) != 0) {
                fprintf(stderr, "%s: No such interface.\n", argv[arg]);
                status = 1;
                continue;
            }

            printf("%s: Interface %d", iface.name, iface.index);
            if (iface.flags & IFF_UP)
                printf(", up");
            if (iface.duplex == DUPLEX_FULL)
                printf(", full duplex");
            else
            if (iface.duplex == DUPLEX_HALF)
                printf(", half duplex");
            if (iface.speed > 0)
                printf(", %ld Mbps", iface.speed);
            printf("\n");
        }

    } else {
        for (arg = 1; ; arg++) {
            if (get_interface_by_index(arg, &iface) != 0)
                break;

            printf("%s: Interface %d", iface.name, iface.index);
            if (iface.flags & IFF_UP)
                printf(", up");
            if (iface.duplex == DUPLEX_FULL)
                printf(", full duplex");
            else
            if (iface.duplex == DUPLEX_HALF)
                printf(", half duplex");
            if (iface.speed > 0)
                printf(", %ld Mbps", iface.speed);
            printf("\n");
        }
    }

    return status;
}

If you save the above as iflist.c, you can compile it using

gcc -W -Wall -O3 iflist.c -o iflist

To see the usage, run iflist -h. To list all interfaces, run it without parameters:

./iflist

The above will use the enumeration method I described. To list only specific interfaces, run it naming the interfaces:

./iflist eth0 lo

Duplex and speed is only listed for ethernet interfaces, of course.


Edited to add:

If the above program does not supply the bandwidth and mode for an interface, here is a simplified version which reports the exact reason (errors). This one takes the interface names as commandline parameters; it does not enumerate interfaces.

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

int ethernet_interface(const char *const name,
                       int *const index, int *const speed, int *const duplex)
{
    struct ifreq        ifr;
    struct ethtool_cmd  cmd;
    int                 fd, result;

    if (!name || !*name) {
        fprintf(stderr, "Error: NULL interface name.\n");
        fflush(stderr);
        return errno = EINVAL;
    }

    if (index)  *index = -1;
    if (speed)  *speed = -1;
    if (duplex) *duplex = -1;

    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1) {
        const int err = errno;
        fprintf(stderr, "%s: Cannot create AF_INET socket: %s.\n", name, strerror(err));
        fflush(stderr);
        return errno = err;
    }

    strncpy(ifr.ifr_name, name, sizeof ifr.ifr_name);
    ifr.ifr_data = (void *)&cmd;
    cmd.cmd = ETHTOOL_GSET;
    if (ioctl(fd, SIOCETHTOOL, &ifr) < 0) {
        const int err = errno;
        do {
            result = close(fd);
        } while (result == -1 && errno == EINTR);
        fprintf(stderr, "%s: SIOCETHTOOL ioctl: %s.\n", name, strerror(err));
        return errno = err;
    }

    if (speed)
        *speed = ethtool_cmd_speed(&cmd);

    if (duplex)
        switch (cmd.duplex) {
        case DUPLEX_HALF: *duplex = 0; break;
        case DUPLEX_FULL: *duplex = 1; break;
        default:
            fprintf(stderr, "%s: Unknown mode (0x%x).\n", name, cmd.duplex);
            fflush(stderr);
            *duplex = -1;
        }

    if (index && ioctl(fd, SIOCGIFINDEX, &ifr) >= 0)
        *index = ifr.ifr_ifindex;

    do {
        result = close(fd);
    } while (result == -1 && errno == EINTR);
    if (result == -1) {
        const int err = errno;
        fprintf(stderr, "%s: Error closing socket: %s.\n", name, strerror(err));
        return errno = err;
    }

    return 0;
}

int main(int argc, char *argv[])
{
    int  arg, speed, index, duplex;

    if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s INTERFACE ...\n", argv[0]);
        fprintf(stderr, "\n");
        return 0;
    }

    for (arg = 1; arg < argc; arg++) {
        if (ethernet_interface(argv[arg], &index, &speed, &duplex))
            return 1;

        if (index == -1)
            printf("%s: (no interface index)", argv[arg]);
        else
            printf("%s: interface %d", argv[arg], index);

        if (speed == -1)
            printf(", unknown bandwidth");
        else
            printf(", %d Mbps bandwidth", speed);

        if (duplex == 0)
            printf(", half duplex.\n");
        else if (duplex == 1)
            printf(", full duplex.\n");
        else
            printf(", unknown mode.\n");
    }

    return 0;
}

Questions?

Nominal Animal
  • 38,216
  • 5
  • 59
  • 86
  • Here is the output I get $ ./iflist lo: Interface 1, up eth0: Interface 2, up – Bruce Jan 11 '13 at 06:15
  • I tried it on some other machines also. I don't get the bandwidth and mode. – Bruce Jan 11 '13 at 06:19
  • @Bruce: The reason for that is that your interfaces do not support the `SIOCETHTOOL` ioctl, which is used to obtain the mode and bandwidth. What kind of interfaces you have on those machines? Infiniband? Does `ethtool eth0` yield the bandwidth and mode on those? – Nominal Animal Jan 11 '13 at 09:21
  • @Bruce: Scratch that; infiniband et cetera do seem to support the ioctls. Could you please compile and run the second example code I just added? After checking on an older RHEL system, I noticed that I get `eth0: SIOCETHTOOL ioctl: Operation not permitted.` if I run it as a non-root user. This is because on some systems only root is permitted to obtain such detailed information on the interfaces. You can verify if this applies to your system too, by running the example code as root (using `su` or `sudo`). – Nominal Animal Jan 11 '13 at 10:18
  • I get the same error "eth0: SIOCETHTOOL ioctl: Operation not supported." even when I run the code as root. – Bruce Jan 11 '13 at 14:47
  • @Bruce: That means your interface does not support the ethtool ioctls. Do you know of any command that actually does yield the bandwidth and mode? Have you tried `ethtool` yet? Do note that for example, virtual machine network interfaces do not really have bandwidth or mode, since they do not make any sense for a virtual interface. Similarly for other virtual and non-ethernet devices like loopback. – Nominal Animal Jan 11 '13 at 16:22
  • ethtool also gives an error. "virtual machine network interfaces do not really have bandwidth or mode" that explains it. Thanks! – Bruce Jan 11 '13 at 17:24
4

(1) getifaddrs()

(2) if_indextoname(), if_nameindex(), if_nametoindex()

(3) I'm not sure about this one but you might be able to get at it through ioctl() and one of the SIOCGIF* parameters or from /proc.

Duck
  • 26,924
  • 5
  • 64
  • 92
2

the following link well explain the getifaddrs function with a working example

getifaddrs()

Davide Berra
  • 6,387
  • 2
  • 29
  • 50
1
ethtool eth1

this command will list all the details about eth1 including speed, duplex, port...

you can use popen() to get the output and map it.

POPEN(3) Linux Programmer's Manual
POPEN(3)

NAME popen, pclose - pipe stream to or from a process

SYNOPSIS #include

   FILE *popen(const char *command, const char *type);
george
  • 139
  • 1
  • 4