Get local ip in C on linux
Local Ip address
The local ip is the source ip in IP packets send out from a system. When the system is a part of a local area network then the local ip in most cases is something like 192.168.*.* . The network interface (eth0) is assigned an ip address of that range. However when the machine is connected to internet through some ppp connection (like dialup modem) then the system has an ip address allotted by the isp directly.
Kernel Routing tables
The kernal maintains routing tables which it uses to decide the default gateway , its interface and the local ip configured for that interface. The /proc/net/route file (not really a file but appears like one) has more information about it.
A typical /proc/net/route output would look like :
$ cat /proc/net/route Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT eth0 0000A8C0 00000000 0001 0 0 1 00FFFFFF 0 0 0 eth0 0000FEA9 00000000 0001 0 0 1000 0000FFFF 0 0 0 eth0 00000000 0100A8C0 0003 0 0 0 00000000 0 0 0
The above lists the interface , destination , gateway etc. The interface (Iface) whose destination is 00000000 is the interface of the default gateway.
Now have a look at the route command output
$ route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 192.168.0.0 0.0.0.0 255.255.255.0 U 1 0 0 eth0 169.254.0.0 0.0.0.0 255.255.0.0 U 1000 0 0 eth0 0.0.0.0 192.168.0.1 0.0.0.0 UG 0 0 0 eth0
Now the gateway for the destination 0.0.0.0 is the default gateway. So from the /proc/net/route output this line is of interest :
eth0 00000000 0100A8C0 0003 0 0 0 00000000 0 0 0
Its destination is 00000000 and gateway is 0100A8C0. The gateway is actually the IP address of the gateway in hex format in reverse order (little endian). Its 01.00.A8.C0 or 1.0.168.192
So by reading that line in a C program we can find out the default gateway and its interface. The IP address of this interface shall be the source ip in IP packets send out from this system.
The code for this is pretty simple as we can see :
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;
}
}
}
The above code prints : "Default interface is : eth0"
Now we need to get the ip address of the default interface eth0. The getnameinfo function can be used for this.
Sample code is found here.
Combining that with our previous code we get :
/*
* Find local ip used as source ip in ip packets.
* Read the /proc/net/route file
*/
#include<stdio.h> //printf
#include<string.h> //memset
#include<errno.h> //errno
#include<sys/socket.h>
#include<netdb.h>
#include<ifaddrs.h>
#include<stdlib.h>
#include<unistd.h>
int main ( int argc , char *argv[] )
{
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;
}
Output
Default interface is : eth0 address: 192.168.0.6
The above method used the kernel routing tables to determine the primary gateway interface and its ip address, which is the local ip address. An alternative method can be used, which does not check the routing tables.
Using getsockname with an external connection
In this method, a program makes a socket connection with some remote server outside the network and then call getsockname on the connected socket. This would return the local ip address.
Code
/*
* Find local ip used as source ip in ip packets.
* Use getsockname and a udp connection
*/
#include<stdio.h> //printf
#include<string.h> //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 ( int argc , char *argv[] )
{
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)
{
perror("Socket error");
}
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) );
struct sockaddr_in name;
socklen_t namelen = sizeof(name);
err = getsockname(sock, (struct sockaddr*) &name, &namelen);
char buffer[100];
const char* p = inet_ntop(AF_INET, &name.sin_addr, buffer, 100);
if(p != NULL)
{
printf("Local ip is : %s \n" , buffer);
}
else
{
//Some error
printf ("Error number : %d . Error message : %s \n" , errno , strerror(errno));
}
close(sock);
return 0;
}
Output
Local ip is : 192.168.0.6