How to get NIC details from a C program?

2020-05-27 14:38发布

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)

标签: c linux
4条回答
▲ chillily
2楼-- · 2020-05-27 15:08

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?

查看更多
兄弟一词,经得起流年.
3楼-- · 2020-05-27 15:08
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);
查看更多
仙女界的扛把子
4楼-- · 2020-05-27 15:11

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

getifaddrs()

查看更多
看我几分像从前
5楼-- · 2020-05-27 15:30

(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.

查看更多
登录 后发表回答