Reputation: 2797
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
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
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
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
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