Friday, October 8, 2010

Programmatic interface alias creation in Linux

In *nix you can create an alias for an interface. So for my ethernet card residing at eth0, whose address is normally 192.168.100.4, I can also create a large number of aliases, for example, 192.168.100.45 for interface eth0:45. I have to give it a unique name not more than 16 bytes long, or it won't be able to distinguish it from my normal interface. Now why would I want to do that? Because, in my case I want to define hundreds or thousands of aliased addresses on my machine, so I can bind on to each of them and send ip-packets from those aliased addresses to a server. That way, I can simulate a DDoS attack of 200 or 5000 machines with just one machine. That's cool. But how do you do it?

You're supposed to use ifconfig, a commandline tool, but since I needed speed I wanted to be able to do it programmatically. How? On BSD you can call ioctl with SIOCAIFADDR to set the ip-address of an interface. But on Linux you have to use SIOCSIFADDR instead. Just give it a unique name and call ioctl with that argument and you're done. Since I didn't see any sample code for this when I Googled, I thought I'd save other people's time by putting this up. It copies the default netmask and broadcast address from the default interface, so you don't have to set these. You'll need to be supervisor to execute it.

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>

int skfd;
int set_ip_using(const char *name, int c, unsigned long ip)
{
    struct ifreq ifr;
    struct sockaddr_in sin;

    strncpy(ifr.ifr_name, name, IFNAMSIZ);
    memset(&sin, 0, sizeof(struct sockaddr));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = ip;
    memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr));
    if (ioctl(skfd, c, &ifr) < 0)
 {
  printf("failed to set ip-address\n");
  exit(0);
    }
 return 0;
}
int main ( int argc, char **argv )
{
 char *ip_address=NULL;
 char *interface;
 if ( argc==3 )
 {
  struct sockaddr_in sin;
  unsigned long ip;
  ip_address=argv[2];
  interface = argv[1];
  skfd = socket( AF_INET, SOCK_DGRAM, 0);
  if ( skfd == -1 )
  {
   printf("Couldn't open socket\n");
   exit(0);
  }
  inet_pton( AF_INET, ip_address, &sin );
  memcpy(&ip, &sin.sin_addr.s_addr, sizeof(unsigned long));  
  set_ip_using( interface,SIOCSIFADDR,ip );
  close( skfd );
 }
 else
  printf("usage: setip <interface-name> <ip-address>\n");
}