Set Interface IP Address From C In Linux

There are a lot of cool things you can do from a C program. One of them is setting an interface’s IP address using ioctl calls. You can of course, set it the old fashioned way using ifconfig, which I wrote about earlier. Rather than having to execute a system call to ifconfig from your C process, you can actually set the interface IP address using an input/output control call (ioctl) much the same way that ifconfig does itself. Holy crap Batman!

It goes to say also, if you can set the IP address then you may also read it via an ioctl call. The example I have crafted includes the function call which takes two parameters, the first is the interface name, the second is the IP address in normal form (192.168.1.1) which is then converted to binary. This function will also bring the interface up prior to setting the IP address. Lets take a look:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <sys/ioctl.h>
#include <linux/sockios.h>
#include <errno.h>
#include <netinet/in.h>
#if defined(__GLIBC__) && __GLIBC__ >=2 && __GLIBC_MINOR__ >= 1
#include <netpacket/packet.h>
#include <net/ethernet.h>
#else
#include <sys/types.h>
#include <netinet/if_ether.h>
#endif
 
int set_ip(char *iface_name, char *ip_addr)
{
	if(!iface_name)
		return -1;	
 
	int sockfd;
	struct ifreq ifr;
	struct sockaddr_in sin;
 
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd == -1){
		fprintf(stderr, "Could not get socket.\n");
		return -1;
	}
 
	/* get interface name */
	strncpy(ifr.ifr_name, iface_name, IFNAMSIZ);
 
	/* Read interface flags */
	if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
		fprintf(stderr, "ifdown: shutdown ");
		perror(ifr.ifr_name);
		return -1;
	}
 
	/*
	* Expected in <net/if.h> according to
	* "UNIX Network Programming".
	*/
	#ifdef ifr_flags
	# define IRFFLAGS       ifr_flags
	#else   /* Present on kFreeBSD */
	# define IRFFLAGS       ifr_flagshigh
	#endif
 
	// If interface is down, bring it up
	if (!(ifr.IRFFLAGS & IFF_UP)) {
		fprintf(stdout, "Device is currently down..setting up.-- %u\n",ifr.IRFFLAGS);
		ifr.IRFFLAGS |= IFF_UP;
		if (ioctl(sockfd, SIOCSIFFLAGS, &ifr) < 0) {
			fprintf(stderr, "ifup: failed ");
			perror(ifr.ifr_name);
			return -1;
		}
	}
 
	sin.sin_family = AF_INET;
 
	// Convert IP from numbers and dots to binary notation
	inet_aton(ip_addr,&sin.sin_addr.s_addr);	
	memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr));	
 
	// Set interface address
	if (ioctl(sockfd, SIOCSIFADDR, &ifr) < 0) {
		fprintf(stderr, "Cannot set IP address. ");
		perror(ifr.ifr_name);
		return -1;
	}	
	#undef IRFFLAGS		
 
	return 0;
}
 
void usage()
{
	const char *usage = {
		"./set_ip [interface] [ip address]\n"
	};
	fprintf(stderr,"%s",usage);
}
 
int main(int argc, char **argv)
{
	if(argc < 3){
		usage();
		return -1;
	}
 
	set_ip(argv[1],argv[2]);
 
	return 0;
}

Lets check if it works. I will do an ifconfig on eth1 to see its current IP address, then use the program above to set it to a new IP address, then use ifconfig again to view it.

$ ifconfig eth1
eth1      Link encap:Ethernet  HWaddr 00:1b:21:0a:d2:cf  
          inet addr:192.168.5.12  Bcast:192.168.5.255  Mask:255.255.255.0
          inet6 addr: fe80::21b:21ff:fe0a:d2cf/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:2690 errors:0 dropped:0 overruns:0 frame:0
          TX packets:14732 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:100 
          RX bytes:516826 (516.8 KB)  TX bytes:2242645 (2.2 MB)
 
$ sudo ./set_ip eth1 12.13.14.15
Device is currently down..setting up.-- 4163
$ ifconfig eth1
eth1      Link encap:Ethernet  HWaddr 00:1b:21:0a:d2:cf  
          inet addr:12.13.14.15  Bcast:12.255.255.255  Mask:255.0.0.0
          inet6 addr: fe80::21b:21ff:fe0a:d2cf/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:2690 errors:0 dropped:0 overruns:0 frame:0
          TX packets:14742 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:100 
          RX bytes:516826 (516.8 KB)  TX bytes:2247309 (2.2 MB)

Because we are modifying an interfaces IP address, we must run this process as root or with the use of sudo. Looks like it works! Of course you may want to set your IP address to something more useful than this. Also not that I do no error checking on user input, you may wish to take that into account if your input comes from an untrusted source.