Sunday, November 7, 2010

Getting/Setting the netmask in Linux/BSD

Recently I have been wrestling with a method for generating multiple aliases for one interface, as in the last post. Part of this was finding suitable ip-addresses to act as aliases. My first idea was to choose free ip-addresses on the local subnet, depending on how big it was, then pinging all possible addresses to find out which ones were already taken. This turned out much more complex than I had thought, unreliable and slow.

Then I had the idea of just expanding the interface's netmask up a level, ignoring all the addresses of the existing subnet without testing them, and generating alias ips that couldn't possibly conflict with any already on the network. To do that I needed to get and set the current netmask programmatically. I didn't find much on the setting (you won't find it in Steven's book, for example). So I wrote my own two routines get_netmask and set_netmask and put them into an example program that even works on BSD:

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

/**
 * @param skfd an open socket
 * @param intf the interface name, e.g. eth0
 * @return the netmask as a string
 */
char *get_netmask( int skfd, char *intf )
{
    struct ifreq ifr;
    ifr.ifr_addr.sa_family = AF_INET;
    strncpy( ifr.ifr_name, intf, IFNAMSIZ-1 );
    if ( ioctl(skfd,SIOCGIFNETMASK,&ifr) == -1 )
    {
        printf("could not read interface %s\n",intf);
        exit( 0 );
    }
    /* display result */
    return inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr);
}
/**
 * @param skfd an open socket
 * @param intf the interface name, e.g. eth0
 * @param newmask the new mask a a string e.g. "255.255.0.0"
 */
void set_netmask( int skfd, char *intf, char *newmask )
{
    struct ifreq ifr;
    unsigned int dst;
    struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
    memset(&ifr, 0, sizeof(ifr));
    sin->sin_family = AF_INET;
    if ( !inet_pton(AF_INET, newmask, &sin->sin_addr) )
    {
        printf("failed to convert netmask\n");
        exit( 0 );
    }
    strncpy( ifr.ifr_name, intf, IFNAMSIZ-1 );
    if ( ioctl(skfd,SIOCSIFNETMASK,&ifr) == -1 )
    {
        printf("could not read interface %s\n",intf);
        exit( 0 );
    }
}
/**
 * main entry routine
 * accepts 2 arguments: the interface name and the new netmask
 * e.g. ./intf eth0 255.255.0.0
 */
int main( int argc, char **argv )
{
    char *str;
    int skfd = socket( AF_INET, SOCK_DGRAM, 0);
    if ( skfd == -1 )
    {
        printf("Couldn't open socket\n");
        exit(0);
    }
    printf("netmask was: %s\n", get_netmask(skfd,argv[1]) );
    set_netmask( skfd, argv[1], argv[2] );
    printf("netmask is now: %s\n", get_netmask(skfd,argv[1]) );
    close( skfd );
    return 0;
}

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");
}

Tuesday, September 28, 2010

Two-way dynamic linking in C

Building a dynamic library (or shared object lib) on Linux is easy. All you do is compile your code with the -fPIC or -fpic flags then use the -shared flag for the link-step:

gcc -fPIC foo.c -shared -o foo.so

Then you can load the dynamic library in your main program by using dl_open and dl_sym to get any global symbol from the library.

But what if you also want your shared object library to link with symbols in the main program? This might be necessary, for example, if the dynamic module wanted to obtain a key from the main program. It would have to call a routine in main just as main is calling routines in the library. In this case you must instruct the main program also to export its symbols during linking:

gcc main.c -ldl -rdynamic -o main

Then, main, when it loads the shared object lib foo, will enable both foo to call routines in main, and main to call routines in foo.

Here's main.c. You can use the last command above to compile it

#include <dlfcn.h>
#include <stdio.h>
int bar()
{
 printf("dynamic lib called bar\n");
 return 0;
}
int main( int argc, char **argv )
{
 void *handle;
 handle = dlopen( "./foo.so", RTLD_GLOBAL|RTLD_LAZY );
 if ( handle != NULL )
 {
  int (*r)(void);
  int res;
  *(void **)(&r) = dlsym(handle, "foo");
  res = (*r)();
  printf("foo returned %d\n",res );
 }
 else
  printf("failed to open foo.so\n");
}

And foo.c. This compiles with the first command above:

#include <stdio.h>
extern int bar();
int foo()
{
 printf("called foo inside lib\n");
 bar();
 return 0;
}

Then run it by calling main:

./main
called foo inside lib
dynamic lib called bar
foo returned 0

Wednesday, May 19, 2010

Capturing packets from an Interface in a MIB

I had to use pcap to capture packets arriving at a particular interface. For that the snmp daemon needs to run as root. You can configure the options for starting up the snmpd program in /etc/default/snmpd. What's not cool, though, is trying to start it up and shut it down. It seems that nanosleep on Linux doesn't work, or rather it only works with a resolution of 1 second. No matter how many nanoseconds you specify nanosleep doesn't sleep until you fill in the tv_sec field. This is, however, more annoying than a show-stopper. I can now start packet capture via pcap_loop and kill it with pcap_breakloop. Stopping takes a few seconds but mostly works. So now we have a MIB that actually detects DDoS attacks!!!

Thursday, May 13, 2010

Average CPU usage for a terminating process

Someone asked me if we can easily compute the average CPU usage of a particular program over its lifetime when the program is scheduled to terminate at some point in the future. At that time it should print out the result. Here's what I came up with:

#!/bin/bash
# get average CPU for a process
total=0
times=0
pid=`pgrep "$1"`
while [ -n "$pid" ]; do
temp=`ps -eo pid,pcpu | grep $pid | awk '{print $2}'`
total=`echo "scale=2;$total+$temp" | bc`
times=$[$times+1]
sleep 1
pid=`pgrep "$1"`
done
average=`echo "scale=2;$total/$times" | bc`
echo "Average cpu usage for $1 is: $average"

To run it give it an argument, which should be the name of the process or part or all of the command that launched it, and background the process, e.g.:

./perprocesscpu.sh "java myprogram" &

So, when "java myprogram" terminates, this script tells me its average CPU usage.

Tuesday, April 27, 2010

Reading from the commandline in C

What's the best way to read the output from a commandline command in C? If you use the int system(char*) function you receive back a return code, not the output. Not much use. To receive the output you could call freopen on STDOUT, then read from the file you redirected it to. But when you close the file (which you must), STDOUT is left in a broken state. There has to be a better way, and there is. You use popen, instead of system. Then, the output of the command is supplied via the FILE handle returned by popen. Here's an example that finds out the memory usage of the firefox process under Linux:


#include <stdio.h>
#include <string.h>
static char sysCmd[256];
/*
 * Issue a system command to get memory usage
 * @return the percent value times 100
 */
int get_memory()
{
    int res = 0;
    float percent;
    sysCmd[0] = 0;
    strcat( sysCmd, "ps aux | awk '{if ($11 ~ /" );
    strcat( sysCmd, "firefox" );
    strcat( sysCmd, "/&& !/awk/) {print $4}}'" );
    FILE *CONSOLE = popen( sysCmd, "r" );
    if ( CONSOLE != NULL )
    {
        res = fscanf( CONSOLE, "%f", &percent );
        if ( res <=0 )
            syslog(LOG_ALERT,"Failed to parse result of %s\n",sysCmd);
        pclose( CONSOLE );
        res = (int)(percent * 100.0f);
    }
    else
        syslog(LOG_ALERT,"Failed to open pipe for %s\n",sysCmd);
    return res;
}

Monday, April 12, 2010

Adding or removing portlets from a page programmatically in Pluto

What I wanted to do in the dosTF portal was to add or remove portlets, which in my case were monitoring applets, in response to the contents of the loaded scenario. If an experiment called for monitoring of a particular SNMP parameter, I wanted to be able to automatically add the monitoring portlet and configure it to monitor the correct parameter on the correct target. But it seems that Pluto is designed rather simply to prevent that. You are supposed to add portlets in the GUI, which kind of defeats the whole purpose of saving the experiment in an XML file. I needed to restore the state of the experiment from the XML and to do that I needed to add or remove portlets from the page programmatically.

Pluto has an undocumented way to do this. There is the PageConfig class used by the Pluto Page Admin portlet. It gets the correct instance of this class from the portlet context:

PortletContext pc = getPortletContext();
DriverConfiguration driverConfig = 
    (DriverConfiguration) pc.getAttribute("driverConfig");
PageConfig config = driverConfig.getPageConfig( "About Apache Pluto" );

The problem with this code is that only the Pluto application has access to the relevant portlet context. You can't get access to it from another application containing another portlet. So, to get around this, I simply copied my ScenarioEditor portlet into the pluto application and hey presto, it works! Removing a portlet is a bit harder. You have to get the portlet ids on the page from the PageConfig and then note the differences before and after. Then record the portlet id of the newly added portlet. It would make more sense if addPortlet just returned the portlet id.

However, configuring the portlet before display seems to be hard with this method. Maybe there is some way but PageConfig doesn't seem to have any knowledge of the 'portlet' itself, just its portlet id. But I'll leave that problem for the next post.