/* Copyright 2004 Renzo Davoli
 * Reseased under the GPLv2 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <limits.h>
#include <dlfcn.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/if_tun.h>

#define TUNTAPPATH "/dev/tap"
#define VDETAPEXEC "vdetap"
#define VDEALLTAP "VDEALLTAP"
#define MAX 10

#if defined(RTLD_NEXT)
#define REAL_LIBC RTLD_NEXT
#else
#define REAL_LIBC ((void *) -1L)
#endif

#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__bsdi__)
typedef unsigned long request_t;
#else
typedef int request_t;
#endif

int tapfd[2] = {-1,-1};
static int tapcount=0;

static struct pidlist {
	pid_t pid;
	struct pidlist *next;
} *plh = NULL, *flh=NULL, pidpool[MAX];

static struct pidlist *plmalloc(void) {
	struct pidlist *rv;
	rv=flh;
	if (rv != NULL) 
		flh=flh->next;
	return rv;
}

static int addpid(int pid) {
	struct pidlist *plp;
	if ((plp=plmalloc ()) != NULL) {
		plp->next=plh;
		plh=plp;
		plp->pid=pid;
		return pid;
	} else {
		kill(pid,SIGTERM);
		return -1;
	}
}

void _init(void)
{
	register int i;
	for (i=1;i<MAX;i++) 
		pidpool[i-1].next= &(pidpool[i]);
	flh=pidpool;
}

void _fini(void)
{
	struct pidlist *plp=plh;
	while (plp != NULL) {
		kill(plp->pid,SIGTERM);
		plp = plp->next;
	}
}

int open(const char *path, int flags, ...)
{
	static int (*func) (const char *, int, mode_t) = NULL;
	char *vdesock;
	int pid;
	va_list ap;
	mode_t data;

	if (!func)
		func = (int (*) (const char *, int, mode_t))
		       dlsym (REAL_LIBC, "open");

	va_start(ap, flags);
	data = va_arg(ap, int);
	va_end(ap);

	if (strcmp(path,TUNTAPPATH)==0 && tapfd[0] == -1) {
		if (socketpair(PF_UNIX, SOCK_DGRAM, 0,tapfd) == 0) {
			char num[5];
			char name[10];
			sprintf(name,"tap%d",tapcount++);
			if (((vdesock=getenv(name)) != NULL)
				||(vdesock=getenv(VDEALLTAP)) != NULL){
				if ((pid=fork()) < 0) { 
					close(tapfd[1]);
					errno=EINVAL;
					return -1;
				} else if (pid > 0) { /*father*/
					if((pid=addpid(pid)) < 0) {
						close(tapfd[0]);
						close(tapfd[1]);
						return -1;
					} else {
						close(tapfd[1]);
						return tapfd[0];
					}
				} else { /*son*/
					plh=NULL;
					close(tapfd[0]);
					sprintf(num,"%d",tapfd[1]);
					execlp(VDETAPEXEC,"-",num,vdesock,(char *) 0);
				}
			}
			return tapfd[0];
		}
		else
			return -1;

	} else
		return (*func)(path, flags, data);
}

int ioctl(int fd, unsigned long int command, ...)
{
	static int (*func) (int, request_t, void *) = NULL;
	int dummy;
	va_list ap;
	char *data;
	struct ifstat *ifs;

	if (!func)
		func = (int (*) (int, request_t, void *))
			dlsym (REAL_LIBC, "ioctl");

	va_start(ap, command);
	data = va_arg(ap, char *);
	va_end(ap);

	if (fd == tapfd[0]) {
		switch(command) {
			case SIOCSIFFLAGS:
			case SIOCADDMULTI:
			case SIOCDELMULTI:
				break;

			case SIOCGIFSTATUS:
				ifs = (struct ifstat *)data;
				dummy = strlen(ifs->ascii);
				if(plh && dummy < sizeof(ifs->ascii))
					snprintf(ifs->ascii + dummy,
						sizeof(ifs->ascii) - dummy,
						"\tOpened by PID %d\n",
						plh[0].pid);
				break;

			default:
				return (*func) (fd, command, data);
		}
	}

	return (*func) (fd, command, data);
}



syntax highlighted by Code2HTML, v. 0.9.1