views:

22534

answers:

9

This Question is almost the same as the previously asked Get the IP Address of local computer-Question. However I need to find the IP address(es) of a Linux Machine.

So: How do I - programmatically in C++ - detect the IP addresses of the linux server my application is running on. The servers will have at least two IP addresses and I need a specific one (the one in a given network (the public one)).

I'm sure there is a simple function to do that - but where?

[EDIT]

To make things a bit clearer:

  • The server will obviously have the "localhost": 127.0.0.1
  • The server will have an internal (management) IP address: 172.16.x.x
  • The server will have an external (public) IP address: 80.190.x.x

I need to find the external IP address to bind my application to it. Obviously I can also bind to INADDR_ANY (and actually that's what I do at the moment). I would prefer to detect the public address, though.

+6  A: 

This page shows a solution using the two functions gethostname() together with gethostbyname(). The former tells you the name of the local machine, the latter looks up the IP address(es) for that name. Just using the latter with "localhost" does not work, that typically just gives you 127.0.0.1.

unwind
That won't distinguish between two interfaces on the same machine.
Paul Tomblin
I don't assign names to each interface. Hell, I don't always assign names to each computer.
Paul Tomblin
Paul: are you sure? Won't gethostbyname() return all the addresses? I assumed the original poster was able to somehow "know" when he found the correct IP address.
unwind
This is unreliable. It works if the /etc/hosts file is setup right, and on Windows. However, it just as often only returns the loopback addresses.
Christopher
+16  A: 
  1. Create a socket.
  2. Perform ioctl(<socketfd>, SIOCGIFCONF, (struct ifconf)&buffer);

Read /usr/include/linux/if.h for information on the ifconf and ifreq structures. This should give you the IP address of each interface on the system. Also read /usr/include/linux/sockios.h for additional ioctls.

Steve Baker
+2  A: 

You can do some integration with curl as something as easy as: curl www.whatismyip.org from the shell will get you your global ip. You're kind of reliant on some external server, but you will always be if you're behind a NAT.

I never understand why more people don't use this site. They even have a machine readable page so you don't have to screen scrap the information.
caspin
You may not get correct IP if you are operating inside proxy.
Jack
+3  A: 

Further to what Steve Baker has said, you can find a description of the SIOCGIFCONF ioctl in the netdevice(7) man page.

Once you have the list of all the IP addresses on the host, you will have to use application specific logic to filter out the addresses you do not want and hope you have one IP address left.

camh
+4  A: 

As you have found out there is no such thing as a single "local IP address". Here's how to find out the local address that can be sent out to a specific host.

  1. Create a UDP socket
  2. Connect the socket to an outside address (the host that will eventually receive the local address)
  3. Use getsockname to get the local address
jjvainio
I've never done it this way but I like it. It uses the OS routing table automatically and is much easier than scanning the interface list.
Zan Lynx
This assumes that you know what an outside address will be. This is often not the case in data centers.
Christopher
+2  A: 

I found the ioctl solution problematic on os x. However getifaddress() will let you do the same thing easily, it works fine for me on os x 10.5 and should be the same below.

I've done a quick example below which will print all of the machine's IPv4 address, would easy enough to make IPv6 compliant (you should also check the getifaddrs was successful ie returns 0).

struct ifaddrs * ifAddrStruct=NULL;
void * tmpAddrPtr=NULL;
int i=0;

getifaddrs(&ifAddrStruct));

while (ifAddrStruct!=NULL) {
  if (ifAddrStruct->ifa_addr->sa_family==AF_INET && strcmp(ifAddrStruct->ifa_name, "lo0")!=0) { // check it is IP4 and not lo0
    // is a valid IP4 Address
    tmpAddrPtr=&((struct sockaddr_in *)ifAddrStruct->ifa_addr)->sin_addr;
    printf("IP Address %s\n", inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, 
  }
  ifAddrStruct=ifAddrStruct->ifa_next;
}
Twelve47
A: 

How do I come to know that IP is added on specific interface such as NIC or VLAN ?

akash
+1  A: 

I like jjvainio's answer. As Zan Lnyx says, it uses the local routing table to find the IP address of the ethernet interface that would be used for a connection to a specific external host. By using a connected UDP socket, you can get the information without actually sending any packets. The approach requires that you choose a specific external host. Most of the time, any well-known public IP should do the trick. I like Google's public DNS server address 8.8.8.8 for this purpose, but there may be times you'd want to choose a different external host IP. Here is some code that illustrates the full approach.

void GetPrimaryIp(char* buffer, size_t buflen) 
{
    assert(buflen >= 16);

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    assert(sock != -1);

    const char* kGoogleDnsIp = "8.8.8.8";
    uint16_t kDnsPort = 53;
    struct sockaddr_in serv;
    memset(&serv, 0, sizeof(serv));
    serv.sin_family = AF_INET;
    serv.sin_addr.s_addr = inet_addr(kGoogleDnsIp);
    serv.sin_port = htons(kDnsPort);

    int err = connect(sock, (const sockaddr*) &serv, sizeof(serv));
    assert(err != -1);

    sockaddr_in name;
    socklen_t namelen = sizeof(name);
    err = getsockname(sock, (sockaddr*) &name, &namelen);
    assert(err != -1);

    const char* p = inet_ntop(AF_INET, &name.sin_addr, buffer, buflen);
    assert(p);

    close(sock);
}
4dan
A: 

Don't hard code it: this is the sort of thing that can change. Many programs figure out what to bind to by reading in a config file, and doing whatever that says. This way, should your program sometime in the future need to bind to something that's not a public IP, it can do so.

Thanatos