Smith Dwayne
Smith Dwayne

Reputation: 2797

Get Local Ip address in c++

I have a code to get local ip address. This is the code I use.

typedef std::map<string,string> settings_t;

void loadLocalIp (settings_t &ipConfig)
{
    struct ifaddrs * ifAddrStruct=NULL;
    struct ifaddrs * ifa=NULL;
    void * tmpAddrPtr=NULL;      

    getifaddrs(&ifAddrStruct);

    for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
        if (ifa ->ifa_addr->sa_family==AF_INET) { // check it is IP4
            // is a valid IP4 Address
            tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
            char addressBuffer[INET_ADDRSTRLEN];
            inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);

            string key(ifa->ifa_name);
            string value(addressBuffer);
            cout<<key<<" =1 " <<value<<endl;
            ipConfig.insert(std::pair<string,string>(key, value));

           // printf("'%s': %s\n", ifa->ifa_name, addressBuffer); 
         }
     }
    if (ifAddrStruct!=NULL) 
        freeifaddrs(ifAddrStruct);//remember to free ifAddrStruct
}

int main()
{
    settings_t ipConfig;
    loadLocalIp(ipConfig);
    cout<<ipConfig.at("enp2s0")<<endl;
    return 0;
}

So My result, is

lo =1 127.0.0.1
enp2s0 =1 172.20.55.6
172.20.55.6

But In another computer, the interface name is different. They get result like bellow,

lo =1 127.0.0.1
ens32 =1 172.20.55.9
terminate called after throwing an instance of 'std::out_of_range'
  what():  map::at
Aborted (core dumped)

I want to get my Ip address whatever the interface name is. How can I get my local ip address if the interface name varies from different computer. It should give the ip address whatever the interface name is. How can I do this?

My question is, Now I am getting my local IP from this method. But I should get IP whatever the Interface name is. One thing, I need to find that interface name and apply it in my above code (or) Is there any other option to find that IP without that interface?

Upvotes: 8

Views: 37667

Answers (4)

SpectreVert
SpectreVert

Reputation: 197

None of these answers seemed good enough: either too much trouble walking through the interfaces or required connection to internet.

Here is a method based upon Justin Randall's answer. It's basically the same but it connects a UDP socket rather than a TCP. As per udp(7), using connect(3) on a unbound UDP socket:

will automatically assign a free local port [...] and bind the socket to INADDR_ANY

Moreover, conversely to a TCP socket, connect(3) on a UDP socket does not present any network overhead or communication, as it only changes the rules regarding which packet to drop and which to keep on the socket buffers.

Therefore, connecting to any IP address that is not INADDR_LOOPBACK is sufficient to retrieve a local address which has been chosen to bind the socket.

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

#include <cstring>
#include <iostream>

int main(void) {
    int sock = socket(PF_INET, SOCK_DGRAM, 0);
    sockaddr_in loopback;

    if (sock == -1) {
        std::cerr << "Could not socket\n";
        return 1;
    }

    std::memset(&loopback, 0, sizeof(loopback));
    loopback.sin_family = AF_INET;
    loopback.sin_addr.s_addr = 1337;   // can be any IP address
    loopback.sin_port = htons(9);      // using debug port

    if (connect(sock, reinterpret_cast<sockaddr*>(&loopback), sizeof(loopback)) == -1) {
        close(sock);
        std::cerr << "Could not connect\n";
        return 1;
    }

    socklen_t addrlen = sizeof(loopback);
    if (getsockname(sock, reinterpret_cast<sockaddr*>(&loopback), &addrlen) == -1) {
        close(sock);
        std::cerr << "Could not getsockname\n";
        return 1;
    }

    close(sock);

    char buf[INET_ADDRSTRLEN];
    if (inet_ntop(AF_INET, &loopback.sin_addr, buf, INET_ADDRSTRLEN) == 0x0) {
        std::cerr << "Could not inet_ntop\n";
        return 1;
    } else {
        std::cout << "Local ip address: " << buf << "\n";
    }
}

Upvotes: 12

Toxic
Toxic

Reputation: 1

Cool method of getting local ip is to execute the ipconfig command, save the output to a file read it, and parse the data so the output only shows your ipv4 address for example. Can be done with something like this:

std::string GetParsedIPConfigData(std::string Columb)
{
    //Set up command file path and command line command
    std::string APPDATA = getenv("APPDATA");
    std::string path = APPDATA + "\\localipdata.txt";
    std::string cmd =  "ipconfig > " + path;

    //execute ipconfig command and save file to path
    system(cmd.c_str());

    //current line
    std::string line;

    //Line array : Here is all lines saved
    std::string lineArray[500];
    int arrayCount = 0;

    std::ifstream file(path);
    if (file.is_open())
    {
        //Get all lines
        while (std::getline(file, line))
        {
            //Save each line into a element in an array
            lineArray[arrayCount] = line;
            arrayCount++;
        }

        for (int arrayindex = 0; arrayindex <= arrayCount; arrayindex++)
        {
            std::string s = Columb;
            std::string s2 = ":";

            //Search all lines and get pos
            std::size_t i = lineArray[arrayindex].find(s);
            std::size_t i2 = lineArray[arrayindex].find(s2);

            //Found a match for Columb
            if (lineArray[arrayindex].find(s) != std::string::npos)
            {
                //Validate
                if (i != std::string::npos)
                {
                    //Earse Columb name
                    lineArray[arrayindex].erase(i, s.length());

                    //Erase all blanks
                    lineArray[arrayindex].erase(remove_if(lineArray[arrayindex].begin(), lineArray[arrayindex].end(), isspace), lineArray[arrayindex].end());

                    //Found match for ':'
                    if (lineArray[arrayindex].find(s2) != std::string::npos)
                    {
                        //Validate
                        if (i2 != std::string::npos)
                        {
                            //Delete all characters prior to ':'
                            lineArray[arrayindex].erase(0, lineArray[arrayindex].find(":"));
                            lineArray[arrayindex].erase(std::remove(lineArray[arrayindex].begin(), lineArray[arrayindex].end(), ':'), lineArray[arrayindex].end());
                        }
                    }
                    //Return our data
                    return lineArray[arrayindex];
                }
            } 

            //Only go through all lines once
            if (arrayindex == arrayCount)
                break; 
        } 

        //Close file
        file.close();
    }
    //Something went wrong
    return "Invalid";
}

And the just call it like so:

cout << parser.GetParsedIPConfigData("IPv4 Address") << "\n\n";

Upvotes: 0

Justin Randall
Justin Randall

Reputation: 2278

I want to get my IP address whatever the interface name is.

It is difficult to reliably get the local ip address by looking at the network interface. As you have already discovered, the network interface name can be unique for each host you run on. To further complicate things, a computer may have multiple network interfaces and each of those may or may not be connected to the Internet.

You don't need to use the default interface. A more simplistic approach is to just let the OS routing table figure it out for you. You can do this by setting up a socket connection to some external server and then calling getsockname to get the local address. This example uses Google's DNS server at 8.8.8.8 to establish a socket connection but you can use whatever external server you'd like.

#include <iostream>     ///< cout
#include <cstring>      ///< memset
#include <errno.h>      ///< errno
#include <sys/socket.h> ///< socket
#include <netinet/in.h> ///< sockaddr_in
#include <arpa/inet.h>  ///< getsockname
#include <unistd.h>     ///< close

int main()
{
    const char* google_dns_server = "8.8.8.8";
    int dns_port = 53;

    struct sockaddr_in serv;
    int sock = socket(AF_INET, SOCK_DGRAM, 0);

    //Socket could not be created
    if(sock < 0)
    {
        std::cout << "Socket error" << std::endl;
    }

    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_addr.s_addr = inet_addr(google_dns_server);
    serv.sin_port = htons(dns_port);

    int err = connect(sock, (const struct sockaddr*)&serv, sizeof(serv));
    if (err < 0)
    {
        std::cout << "Error number: " << errno
            << ". Error message: " << strerror(errno) << std::endl;
    }

    struct sockaddr_in name;
    socklen_t namelen = sizeof(name);
    err = getsockname(sock, (struct sockaddr*)&name, &namelen);

    char buffer[80];
    const char* p = inet_ntop(AF_INET, &name.sin_addr, buffer, 80);
    if(p != NULL)
    {
        std::cout << "Local IP address is: " << buffer << std::endl;
    }
    else
    {
        std::cout << "Error number: " << errno
            << ". Error message: " << strerror(errno) << std::endl;
    }

    close(sock);
    return 0;
}

Upvotes: 19

Smith Dwayne
Smith Dwayne

Reputation: 2797

Thank you for your solution. It works fine. But When I search for solution, I came up with the following answer also. Please have a look at it. What is pros and cons of this answer.

 FILE *f;
    char line[100] , *p , *c;

    f = fopen("/proc/net/route" , "r");

    while(fgets(line , 100 , f))
    {
        p = strtok(line , " \t");
        c = strtok(NULL , " \t");

        if(p!=NULL && c!=NULL)
        {
            if(strcmp(c , "00000000") == 0)
            {
                printf("Default interface is : %s \n" , p);
                break;
            }
        }
    }

    //which family do we require , AF_INET or AF_INET6
    int fm = AF_INET;
    struct ifaddrs *ifaddr, *ifa;
    int family , s;
    char host[NI_MAXHOST];

    if (getifaddrs(&ifaddr) == -1) 
    {
        perror("getifaddrs");
        exit(EXIT_FAILURE);
    }

    //Walk through linked list, maintaining head pointer so we can free list later
    for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) 
    {
        if (ifa->ifa_addr == NULL)
        {
            continue;
        }

        family = ifa->ifa_addr->sa_family;

        if(strcmp( ifa->ifa_name , p) == 0)
        {
            if (family == fm) 
            {
                s = getnameinfo( ifa->ifa_addr, (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6) , host , NI_MAXHOST , NULL , 0 , NI_NUMERICHOST);

                if (s != 0) 
                {
                    printf("getnameinfo() failed: %s\n", gai_strerror(s));
                    exit(EXIT_FAILURE);
                }

                printf("address: %s", host);
            }
            printf("\n");
        }
    }

    freeifaddrs(ifaddr);

    return 0;

Upvotes: 0

Related Questions