How to Get Local IP in C using sockets on Linux

By | July 28, 2020

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

To get the local ip address we first need to find the default network interface that is being used for network communication. Then we can find the ip address of that interface which shall be the local ip address.

The kernel 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.

Code

There are 2 parts to the code. First is to get the default network interface name and then get the ip address of the interface.

The following code gets the default interface name.

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"

Get IP address of the interface

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
About Silver Moon

A Tech Enthusiast, Blogger, Linux Fan and a Software Developer. Writes about Computer hardware, Linux and Open Source software and coding in Python, Php and Javascript. He can be reached at [email protected].

3 Comments

How to Get Local IP in C using sockets on Linux
  1. Frak

    When using it with IPv6 addresses, the string %eth0 is appended to the address:

    address: fe80::4a00:33ff:fef4:400%eth0

Leave a Reply

Your email address will not be published. Required fields are marked *