Sunday, December 26, 2010

Get list of network interfaces using rtnetlink sockets

Get list of network interfaces using rtnetlink sockets

/**
 * @file get_if_list.c
 *
 * @brief Print the list of network interfaces available on the system.
 *
 * The network interface list is obtained using rtnetlink sockets.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/socket.h>

#include <net/if.h>

#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>


#define IF_LIST_SIZE 10


static int32_t create_rtnetlink_sock(int32_t *sd);
static int32_t send_if_list_req(int32_t sd);
static int32_t recv_if_list_resp(int32_t sd,
                                 char iflist[IF_LIST_SIZE][IF_NAMESIZE]);

int main(void)
{
        char iflist[IF_LIST_SIZE][IF_NAMESIZE];
        int32_t sd;
        int32_t ret;

        ret = create_rtnetlink_sock(&sd);
        if (ret == -1) {
                fprintf(stderr, "Unable to create rtnetlink socket.\n");
                goto out1;
        }

        ret = send_if_list_req(sd);
        if (ret == -1) {
                fprintf(stderr, "Unable to send GET INTERFACE request.\n");
                goto out2;
        }

        ret = recv_if_list_resp(sd, iflist);
        if (ret == -1) {
                fprintf(stderr, "Unable to receive INTERFACE list request.\n");
                goto out2;
        }

        close(sd);

        exit(0);
out2:
        close(sd);
out1:
        exit(1);
}

/**
 * @brief Create a rtnetlink socket.
 *
 * @param[out] sd Will be filled with newly created socket's descriptor.
 *
 * @return 0 on success; -1 on failure.
 */
static int32_t create_rtnetlink_sock(int32_t *sd)
{
        struct sockaddr_nl nl;
        int32_t ret;

        if (sd == NULL) {
                fprintf(stderr, "%s: Invalid arguments.\n", __func__);
                goto out1;
        }

        *sd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
        if (*sd == -1) {
                perror("socket");
                goto out1;
        }

        memset(&nl, 0, sizeof(nl));
        nl.nl_family = AF_NETLINK;
        ret = bind(*sd, (struct sockaddr *)&nl, sizeof(nl));
        if (ret == -1) {
                perror("bind");
                goto out2;
        }

        return 0;

out2:
        close(*sd);
out1:
        return -1;
}


/**
 * @brief Construct and send a request for obtaining the list of
 * network interfaces.
 *
 * @param[in] sd rtnetlink socket descriptor to be used for communication.
 *
 * @return 0 on success; -1 on failure.
 */
static int32_t send_if_list_req(int32_t sd)
{
        struct {
                struct nlmsghdr nh;
                struct ifinfomsg ifinfomsg;
        } req;
        ssize_t ssize;

        memset(&req, 0, sizeof(req));
        req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(req.ifinfomsg));
        req.nh.nlmsg_type = RTM_GETLINK;
        req.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
        req.ifinfomsg.ifi_family = AF_UNSPEC;

        ssize = send(sd, &req, sizeof(req), 0);
        if (ssize == -1) {
                perror("send");
                return -1;
        }

        return 0;
}


/**
 * @brief Receive and process the rtnetlink response (containing the list
 * of interfaces).
 *
 * @param[in] sd rtnetlink socket descriptor to be used for communication.
 *
 * @return 0 on success; -1 on failure.
 */
static int32_t recv_if_list_resp(int32_t sd,
                                 char iflist[IF_LIST_SIZE][IF_NAMESIZE])
{
        struct ifinfomsg *ifinfomsg;
        struct nlmsghdr *nh;
        struct nlmsgerr *nlme;
        struct rtattr *rta;
        struct {
                struct nlmsghdr nh;
                uint8_t payload[1024];
        } resp;
        ssize_t ssize;
        int32_t len;
        int32_t attr_len;

        ssize = recv(sd, &resp, sizeof(resp), 0);
        if (ssize == -1) {
                perror("recv");
                return -1;
        }

        nh = &(resp.nh);
        if (nh->nlmsg_type == NLMSG_ERROR) {
                nlme = NLMSG_DATA(nh);
                fprintf(stderr, "Got error %d.\n", nlme->error);
                return -1;
        }

        len = ssize;
        printf("Available interfaces:\n");
        for (;NLMSG_OK(&(resp.nh), len); nh = NLMSG_NEXT(nh, len)) {
                ifinfomsg = NLMSG_DATA(nh);
                rta = IFLA_RTA(ifinfomsg);
                attr_len = IFLA_PAYLOAD(nh);
                for (; RTA_OK(rta, attr_len); rta = RTA_NEXT(rta, attr_len)) {
                        if (rta->rta_type == IFLA_IFNAME) {
                                printf("%s ", (char *)RTA_DATA(rta));
                        }
                }
                if (nh->nlmsg_type == NLMSG_DONE) {
                        break;
                }
        }

        printf("\n");

        return 0;
}

Compile and run the program as shown below:

[mwnn@schumi rtnetlink]$ cc -g -Wall get_if_list.c -oget_if_list
[mwnn@schumi rtnetlink]$ ./get_if_list
Available interfaces:
lo eth0

HTML generated by org-mode 7.3 in emacs 23

1 comment: