GRIC and iPass Daemons - virtual hosting

Why?

This page details how to virtual host the GRIC and iPass RADIUS proxies. A question some would ask is - why? The answer is simple - we wanted to be able to identify GRIC and iPass requests by the IP the request was coming from, even though they were running on the same server as our central RADIUS proxy and RADIUS daemon (which have since been integrated into one perl-based RADIUS server, but it still identifies GRIC vs iPass by the IP address of the request source).

GRIC

This was fairly easy to do, just an override on the bind() syscall.

roamctl
startup/shutdown script for GRIC's aims_roam_svd (note the LD_PRELOAD value).
#!/bin/sh

cd /etc/gricrad/executables

case "$1" in

  start)
# DL: Override IP address
LD_PRELOAD=/etc/gricrad/executables/bind_override.so
export LD_PRELOAD
#
#Start roaming with no debug
#/etc/gricrad/executables/aims_roam_svd -d /etc/gricrad/config_files/ -a /var/adm/gricacct -R 1645 -A 1646 -U -e -c -l -t 10 &
#
#Start roaming with terminal debug
/etc/gricrad/executables/aims_roam_svd -d /etc/gricrad/config_files/ -a /var/adm/gricacct -R 1645 -A 1646 -U -x -e -c -l -t 10 &
#
#Start roaming with debug.log debug
#/etc/gricrad/executables/aims_roam_svd -d /etc/gricrad/config_files/ -a /var/adm/gricacct -R 1645 -A 1646 -U -x -e -c -l -t 10 > /etc/gricrad/debug.log &
  ;;

  stop)
# DL: Kill GRIC processes
/usr/bin/ps -u root | egrep 'aims_roa|Relog' | awk '{print $1}' | xargs kill -9
  ;;

  *)
echo 'This script only supports "start" or "stop"'
  ;;

esac
bind_override.c
Note: This code isn't pretty - it doesn't even check sin_family to make sure the bind is a TCP/IP bind. But it works for the GRIC aims_roam_svd.

Compile the following (bind_override.c) with gcc -c -shared bind_override.c -o bind_override.so after changing the IP in it (note: tested on Solaris, should also work on Linux).

#ifdef __linux__
#include <asm/unistd.h>
#include <linux/net.h>
static int errno;
static __inline__ _syscall2(int,socketcall,int,a1,unsigned long *,args)
#endif

struct in_addr {
  unsigned int    s_addr;
};

struct sockaddr_in {
  unsigned short  sin_family;
  unsigned short  sin_port;
  struct in_addr  sin_addr;
  char            sin_zero[8];
};

int bind(int fd, struct sockaddr_in *s, int len) {
#ifdef __linux__
  unsigned long sargs[3] = { fd, (long)s, len };
#endif

  if (len >= 8 && s->sin_addr.s_addr == 0) {
    printf("DL: Re-writing bind() syscall\n");
    s->sin_addr.s_addr = inet_addr("192.168.0.2");
#ifdef __linux__
    return socketcall(SYS_BIND, &sargs[0]);
#else
    return _bind(fd, s, len);
#endif
  } else {
    printf("DL: bind() syscall already has explicit addr (%s)\n", inet_ntoa(s->sin_addr));
#ifdef __linux__
    return socketcall(SYS_BIND, &sargs[0]);
#else
    return _bind(fd, s, len);
#endif
  }
}

iPass

Here comes the fun! Well, a bit more logic was required here. The iPass daemons use more system calls and run in different ways, spawn sub-processes which aren't linked against NSL and socket libraries, and so on. Put all of this in your /usr/ipass/bin; you'll have to explicitly load the preloads when running the check-ipass and check-vnas test programs.

make-so
This script compiles and links as required.
#!/bin/sh

# Verbose
gcc -c -shared -o virtual.so virtual.c
ld -r -dy -G virtual.so /usr/lib/libnsl.so /usr/lib/libsocket.so -o virtual_linked.so

# Quiet
gcc -c -shared -o virtual_quiet.so virtual_quiet.c
ld -r -dy -G virtual_quiet.so /usr/lib/libnsl.so /usr/lib/libsocket.so -o virtual_quiet_linked.so
virtual.c
The preload, similar to bind_override but with more test cases.
#include <sys/select.h>

#define VIP "192.168.0.3"

#define AF_INET 2
#define SIN(x) ((struct sockaddr_in *)(x))

struct in_addr {
  unsigned int    s_addr;
};

struct sockaddr_in {
  unsigned short  sin_family;
  unsigned short  sin_port;
  struct in_addr  sin_addr;
  char            sin_zero[8];
};

fd_set __v_bound;
int __v_init = 0;
struct sockaddr_in __v_addr;
int __v_len = sizeof(__v_addr);

static void __v_maybe_init(void) {
  if (!__v_init) {
    __v_init = 1;
    FD_ZERO(&__v_bound);
  }
  bzero(__v_addr, __v_len);
  __v_addr.sin_family = AF_INET;
  __v_addr.sin_addr.s_addr = inet_addr(VIP);
}

int bind(int fd, void *s, size_t len) {
  __v_maybe_init();
  FD_SET(fd, &__v_bound);
  if (len >= 8 && SIN(s)->sin_family == AF_INET && SIN(s)->sin_addr.s_addr == 0) {
    printf("FD %d: Re-writing bind() syscall\n", fd);
    SIN(s)->sin_addr.s_addr = __v_addr.sin_addr.s_addr;
    return _bind(fd, s, len);
  } else {
    printf("FD %d: bind() syscall already has explicit addr (%s)\n", fd, inet_ntoa(SIN(s)->sin_addr));
    return _bind(fd, s, len);
  }
}

int close(int fd) {
  __v_maybe_init();
  if (fd > 0) {
    FD_CLR(fd, &__v_bound);
  }
  return _close(fd);
}

int connect(int fd, void *s, int len) {
  __v_maybe_init();
  if (!FD_ISSET(fd, &__v_bound) && (len >= 8) && SIN(s)->sin_family == AF_INET) {

    printf("FD %d: connect() without bind, binding\n", fd);
    __v_addr.sin_port = 0;
    _bind(fd, &__v_addr, __v_len);
  }
  FD_SET(fd, &__v_bound);
  return _connect(fd, s, len);
}
virtual_quiet.c
A quiet version of the virtual preload, since vnas runs from inetd so we can't output anything.
#include <sys/select.h>

#define VIP "192.168.0.3"

#define AF_INET 2
#define SIN(x) ((struct sockaddr_in *)(x))

struct in_addr {
  unsigned int    s_addr;
};

struct sockaddr_in {
  unsigned short  sin_family;
  unsigned short  sin_port;
  struct in_addr  sin_addr;
  char            sin_zero[8];
};

fd_set __v_bound;
int __v_init = 0;
struct sockaddr_in __v_addr;
int __v_len = sizeof(__v_addr);

static void __v_maybe_init(void) {
  if (!__v_init) {
    __v_init = 1;
    FD_ZERO(&__v_bound);
  }
  bzero(__v_addr, __v_len);
  __v_addr.sin_family = AF_INET;
  __v_addr.sin_addr.s_addr = inet_addr(VIP);
}

int bind(int fd, void *s, size_t len) {
  __v_maybe_init();
  FD_SET(fd, &__v_bound);
  if (len >= 8 && SIN(s)->sin_family == AF_INET && SIN(s)->sin_addr.s_addr == 0) {
    SIN(s)->sin_addr.s_addr = __v_addr.sin_addr.s_addr;
    return _bind(fd, s, len);
  } else {
    return _bind(fd, s, len);
  }
}

int close(int fd) {
  __v_maybe_init();
  if (fd > 0) {
    FD_CLR(fd, &__v_bound);
  }
  return _close(fd);
}

int connect(int fd, void *s, int len) {
  __v_maybe_init();
  if (!FD_ISSET(fd, &__v_bound) && (len >= 8) && SIN(s)->sin_family == AF_INET) {

    __v_addr.sin_port = 0;
    _bind(fd, &__v_addr, __v_len);
  }
  FD_SET(fd, &__v_bound);
  return _connect(fd, s, len);
}
vnas
vnas is moved to vnas.real, and this script put in it's place.
#!/bin/sh
LD_PRELOAD=/usr/ipass/bin/virtual_quiet_linked.so
export LD_PRELOAD
exec /usr/ipass/bin/vnas.real $*
rc.netserverd
The rc.netserverd needs these lines added just above the line starting netserver.
LD_PRELOAD=/usr/ipass/bin/virtual_linked.so
export LD_PRELOAD

--
David Luyer <david@luyer.net>
HTML 3.2 Checked!