user1846251
user1846251

Reputation: 45

C code to get the interface name for the IP address in Linux

How can I get the interface name for the IP address in linux from C code ?

e.g. I'd like to get the interface name ( like etho , eth1 , l0 ) assigned for the IP address 192.168.0.1

Upvotes: 0

Views: 5748

Answers (3)

sparticvs
sparticvs

Reputation: 592

You can use getifaddrs. See man 3 getifaddrs for usage information. This will only work on a Unix-like systems.

Upvotes: 2

Tony
Tony

Reputation: 1657

netlink is a way to do this on Linux. I think it might even be a proper way to do it on Linux (even though it isn't portable).

The strategy is:

  1. Get a list of addresses on interfaces from the kernel by sending a netlink message.
  2. Find the address you want (I have hard coded the one I want as address_dq) and record its interface (a number at this stage)
  3. Get a list of interfaces by sending another netlink message,
  4. Find the number of the interface matching the number you recorded in step (2).
  5. Get the name of the interface.

The code below is not pretty, but I'm sure you could do a better job of it. I have been a especially sloppy by not checking for a multipart message (checking for the NLM_F_MULTI flag and for a message type of NLMSG_DONE is the way to do it). Instead I have just assumed the response to the first message is multipart -- it is on my machine -- and chewed up the NLMSG_DONE message which follows.

Code...

#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/socket.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, void ** argv) {

// This is the address we want the interface name for,
// expressed in dotted-quad format
char * address_dq = "127.0.0.1";
// Convert it to decimal format
unsigned int address;
inet_pton(AF_INET, address_dq, &address);

char buf[16384];

// Our first message will be a header followed by an address payload
struct {
    struct nlmsghdr nlhdr;
    struct ifaddrmsg addrmsg;
} msg;

// Our second message will be a header followed by a link payload
struct {
    struct nlmsghdr nlhdr;
    struct ifinfomsg infomsg;
} msg2;

struct nlmsghdr *retmsg;

// Set up the netlink socket
int sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);

// Fill in the message
// NLM_F_REQUEST means we are asking the kernel for data
// NLM_F_ROOT means provide all the addresses
// RTM_GETADDR means we want address information
// AF_INET means limit the response to ipv4 addresses
memset(&msg, 0, sizeof(msg));
msg.nlhdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
msg.nlhdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
msg.nlhdr.nlmsg_type = RTM_GETADDR;
msg.addrmsg.ifa_family = AF_INET;

// As above, but RTM_GETLINK means we want link information
memset(&msg2, 0, sizeof(msg2));
msg2.nlhdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
msg2.nlhdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
msg2.nlhdr.nlmsg_type = RTM_GETLINK;
msg2.infomsg.ifi_family = AF_UNSPEC;

// Send the first netlink message
send(sock, &msg, msg.nlhdr.nlmsg_len, 0);

int len;

// Get the netlink reply
len = recv(sock, buf, sizeof(buf), 0);

retmsg = (struct nlmsghdr *)buf;

// Loop through the reply messages (one for each address)
// Each message has a ifaddrmsg structure in it, which 
// contains the prefix length as a member.  The ifaddrmsg
// structure is followed by one or more rtattr structures, 
// some of which (should) contain raw addresses.
while NLMSG_OK(retmsg, len) {

    struct ifaddrmsg *retaddr;
    retaddr = (struct ifaddrmsg *)NLMSG_DATA(retmsg);
    int iface_idx = retaddr->ifa_index;

    struct rtattr *retrta;
    retrta = (struct rtattr *)IFA_RTA(retaddr);

    int attlen;
    attlen = IFA_PAYLOAD(retmsg);

    char pradd[128];

    // Loop through the routing information to look for the 
    // raw address.
    while RTA_OK(retrta, attlen) {
        if (retrta->rta_type == IFA_ADDRESS) {
            // Found one -- is it the one we want?
            unsigned int * tmp = RTA_DATA(retrta);
            if (address == *tmp) {
                // Yes!
                inet_ntop(AF_INET, RTA_DATA(retrta), pradd, sizeof(pradd));
                printf("Address %s ", pradd);
                // Now we need to get the interface information
                // First eat up the "DONE" message waiting for us
                len = recv(sock, buf, sizeof(buf), 0);
                // Send the second netlink message and get the reply
                send(sock, &msg2, msg2.nlhdr.nlmsg_len, 0);
                len = recv(sock, buf, sizeof(buf), 0);
                retmsg = (struct nlmsghdr *)buf;
                while NLMSG_OK(retmsg, len) {
                    struct ifinfomsg *retinfo;
                    retinfo = NLMSG_DATA(retmsg);
                    if (retinfo->ifi_index == iface_idx) {
                        retrta = IFLA_RTA(retinfo);
                        attlen = IFLA_PAYLOAD(retmsg);
                        char prname[128];
                        // Loop through the routing information 
                        // to look for the interface name.
                        while RTA_OK(retrta, attlen) {
                            if (retrta->rta_type == IFLA_IFNAME) {
                                strcpy(prname, RTA_DATA(retrta));
                                printf("on %s\n", prname);
                                exit(EXIT_SUCCESS);
                            }
                            retrta = RTA_NEXT(retrta, attlen);

                        }
                    }

                    retmsg = NLMSG_NEXT(retmsg, len);       
                }

            }
        }
        retrta = RTA_NEXT(retrta, attlen);

    }

    retmsg = NLMSG_NEXT(retmsg, len);       
}

}

When run as above, returns Address 127.0.0.1 on lo.

Using "192.168.1.x" instead of "127.0.0.1" it instead returns Address 192.168.1.x on eth0.

Upvotes: 1

technosaurus
technosaurus

Reputation: 7802

Using /proc/net/arp you can match it. Here is a command line tool example.

usage: getdevicebyip 192.168.0.1

#include <stdio.h>
#include <fcntl.h>

int main(int argc, char **argv){
    if (argc < 2) return 1;
    FILE *fp = fopen("/proc/net/arp", "r");
    char ip[99], hw[99], flags[99], mac[99], mask[99], dev[99], dummy[99];
    fgets(dummy, 99, fp); //header line
    while (fscanf(fp, "%s %s %s %s %s %s\n", ip, hw, flags, mac, mask, dev) != EOF)
        if (!strcmp(argv[1],ip))
            printf("%s\n",dev);
    return 0;
}

Upvotes: 3

Related Questions