Logo Search packages:      
Sourcecode: vde2 version File versions  Download package

vde_plug.c

/* Copyright 2002 Renzo Davoli 
 * Licensed under the GPL
 * Modified by Ludovico Gardenghi 2005
 */

#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <stdint.h>
#include <getopt.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <sys/poll.h>
#include <sys/utsname.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <vde.h>
#include <libvdeplug/libvdeplug.h>
#ifdef VDE_IP_LOG
#define DO_SYSLOG
#endif
#ifdef DO_SYSLOG
#include <syslog.h>
#include <ctype.h>
#include <arpa/inet.h>
#endif

#ifndef MIN
#define MIN(X,Y) (((X)<(Y))?(X):(Y))
#endif

#define BUFSIZE 2048
#define ETH_ALEN 6

VDECONN *conn;

struct utsname me;
#define myname me.nodename

static struct passwd *callerpwd;
#ifdef DO_SYSLOG
static char host[256];

void write_syslog_entry(char *message)
{
      char *ssh_client;
      size_t ip_length;

      openlog("vde_plug", 0, LOG_USER);

      //get the caller IP address
      //TNX Giordani-Macchia code from vish.c
      if ((ssh_client=getenv("SSH_CLIENT"))!=NULL)
      {
            for (ip_length=0;ip_length<sizeof(host)&&ssh_client[ip_length]!=0&&!isspace(ssh_client[ip_length]);ip_length++);
            if (ip_length>=sizeof(host))
                  ip_length=sizeof(host)-1;
            memcpy(host,ssh_client,ip_length);
            host[ip_length]=0;
      }
      else
            strcpy(host,"UNKNOWN_IP_ADDRESS");
      syslog(LOG_INFO,"%s: user %s IP %s",message,callerpwd->pw_name,host);
      closelog();
}

void write_syslog_close()
{
      write_syslog_entry("STOP");
}
#endif

#ifdef VDE_IP_LOG
#define MAX_IP 256
int vde_ip_log;

struct header {
      unsigned char dest[ETH_ALEN];
      unsigned char src[ETH_ALEN];
      unsigned char proto[2];
};

union body {
      struct {
            unsigned char version;
            unsigned char filler[11];
            unsigned char ip4src[4];
            unsigned char ip4dst[4];
      } v4;
      struct {
            unsigned char version;
            unsigned char filler[7];
            unsigned char ip6src[16];
            unsigned char ip6dst[16];
      } v6;
      struct {
            unsigned char priovlan[2];
      } vlan;
};

unsigned char ip4list[MAX_IP][4];
unsigned char ip6list[MAX_IP][16];
static unsigned char nulladdr[16];

static int hash4(unsigned char *addr)
{
      return((addr[0]+2*addr[1]+3*addr[2]+5*addr[3]) % MAX_IP);
}

static int hash6(unsigned char *addr)
{
      return((addr[0]+2*addr[1]+3*addr[2]+5*addr[3]+
                        7*addr[4]+11*addr[5]+13*addr[6]+17*addr[7]+
                        19*addr[8]+23*addr[9]+29*addr[10]+31*addr[11]+
                        37*addr[12]+41*addr[13]+43*addr[14]+47*addr[15]) % MAX_IP);
}

static void vde_ip_check(const unsigned char *buf,int rnx) 
{
      struct header *ph=(struct header *) buf;
      register int i,j,vlan=0;
      char addr[256];
      union body *pb;

      pb=(union body *)(ph+1);
      if (ph->proto[0]==0x81 && ph->proto[1]==0x00) { /*VLAN*/
            vlan=((pb->vlan.priovlan[0] << 8) + pb->vlan.priovlan[1]) & 0xfff;
            pb=(union body *)(((char *)pb)+4);
      }
      if (ph->proto[0]==0x08 && ph->proto[1]==0x00 && 
                  pb->v4.version == 0x45) {
            /*v4 */ 
            i=hash4(pb->v4.ip4src);
            j=(i+MAX_IP-1)%MAX_IP;
            while (1) {
                  /* most frequent case first */
                  if (memcmp(pb->v4.ip4src,ip4list[i],4) == 0)
                        break;
                  else if (memcmp(ip4list[i],nulladdr,4) == 0) {
                        memcpy(ip4list[i],pb->v4.ip4src,4);
                        syslog(LOG_INFO,"user %s Real-IP %s has got VDE-IP4 %s on vlan %d",callerpwd->pw_name,host,inet_ntop(AF_INET,ip4list[i],addr,256),vlan);
                        /*new ipv4*/
                        break;
                  } else if (i==j) {
                        syslog(LOG_ERR,"IPv4 table full. Exiting\n");
                        /*full table*/
                        exit(-1);
                  } else 
                        i= (i+1)%MAX_IP;
            }
      }
      else if (ph->proto[0]==0x86 && ph->proto[1]==0xdd && 
                  pb->v4.version == 0x60) {
            /* v6 */
            i=hash6(pb->v6.ip6src);
            j=(i+MAX_IP-1)%MAX_IP;
            while (1) {
                  /* most frequent case first */
                  if (memcmp(pb->v6.ip6src,ip6list[i],16) == 0)
                        break;
                  else if (memcmp(ip6list[i],nulladdr,16) == 0) {
                        memcpy(ip6list[i],pb->v6.ip6src,16);
                        syslog(LOG_INFO,"user %s Real-IP %s has got VDE-IP6 %s on vlan %d",callerpwd->pw_name,host,inet_ntop(AF_INET6,ip6list[i],addr,256),vlan);
                        /*new ipv6*/
                        break;
                  } else if (i==j) {
                        syslog(LOG_ERR,"IPv6 table full. Exiting\n");
                        /*full table*/
                        exit(-1);
                  } else 
                        i= (i+1)%MAX_IP;
            }
      }
}
#endif

unsigned char bufin[BUFSIZE];

void splitpacket(const unsigned char *buf,int size,VDECONN *conn)
{
      static char fragment[BUFSIZE];
      static char *fragp;
      static unsigned int rnx,remaining;

      //fprintf(stderr,"%s: splitpacket rnx=%d remaining=%d size=%d\n",myname,rnx,remaining,size);
      if (size==0) return;
      if (rnx>0) {
            register int amount=MIN(remaining,size);
            //fprintf(stderr,"%s: fragment amount %d\n",myname,amount);
            memcpy(fragp,buf,amount);
            remaining-=amount;
            fragp+=amount;
            buf+=amount;
            size-=amount;
            if (remaining==0) {
                  //fprintf(stderr,"%s: delivered defrag %d\n",myname,rnx);
                  //send(fd,fragment,rnx,0);
#ifdef VDE_IP_LOG
                  if (vde_ip_log)
                        vde_ip_check(buf,rnx);
#endif
                  vde_send(conn,fragment,rnx,0);
                  rnx=0;
            }
      }
      while (size > 0) {
            rnx=(buf[0]<<8)+buf[1];
            size-=2;
            //fprintf(stderr,"%s %d: packet %d size %d %x %x\n",myname,getpid(),rnx,size,buf[0],buf[1]);
            buf+=2;
            if (rnx>1521) {
                  fprintf(stderr,"%s: Packet length error size %d rnx %d\n",myname,size,rnx);
                  rnx=0;
                  return;
            }
            if (rnx > size) {
                  //fprintf(stderr,"%s: begin defrag %d\n",myname,rnx);
                  fragp=fragment;
                  memcpy(fragp,buf,size);
                  remaining=rnx-size;
                  fragp+=size;
                  size=0;
            } else {
                  //fprintf(stderr,"%s: deliver %d\n",myname,rnx);
                  //send(fd,buf,rnx,0);
#ifdef VDE_IP_LOG
                  if (vde_ip_log)
                        vde_ip_check(buf,rnx);
#endif
                  vde_send(conn,(char *)buf,rnx,0);
                  buf+=rnx;
                  size-=rnx;
                  rnx=0;
            }
      }
}

static void cleanup(void)
{
  vde_close(conn);
}

static void sig_handler(int sig)
{
      cleanup();
      signal(sig, SIG_DFL);
      kill(getpid(), sig);
}

static void setsighandlers()
{
      /* setting signal handlers.
       * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
       * ignores all the others signals which could cause termination. */
      struct { int sig; const char *name; int ignore; } signals[] = {
            { SIGHUP, "SIGHUP", 0 },
            { SIGINT, "SIGINT", 0 },
            { SIGPIPE, "SIGPIPE", 1 },
            { SIGALRM, "SIGALRM", 1 },
            { SIGTERM, "SIGTERM", 0 },
            { SIGUSR1, "SIGUSR1", 1 },
            { SIGUSR2, "SIGUSR2", 1 },
            { SIGPROF, "SIGPROF", 1 },
            { SIGVTALRM, "SIGVTALRM", 1 },
#ifdef VDE_LINUX
            { SIGPOLL, "SIGPOLL", 1 },
#ifdef SIGSTKFLT
            { SIGSTKFLT, "SIGSTKFLT", 1 },
#endif
            { SIGIO, "SIGIO", 1 },
            { SIGPWR, "SIGPWR", 1 },
#ifdef SIGUNUSED
            { SIGUNUSED, "SIGUNUSED", 1 },
#endif
#endif
#ifdef VDE_DARWIN
            { SIGXCPU, "SIGXCPU", 1 },
            { SIGXFSZ, "SIGXFSZ", 1 },
#endif
            { 0, NULL, 0 }
      };

      int i;
      for(i = 0; signals[i].sig != 0; i++)
            if(signal(signals[i].sig,
                              signals[i].ignore ? SIG_IGN : sig_handler) < 0)
                  perror("Setting handler");
}

struct pollfd pollv[]={{STDIN_FILENO,POLLIN|POLLHUP},{0,POLLIN|POLLHUP},{0,POLLIN|POLLHUP}};

static void netusage() {
#ifdef DO_SYSLOG
      write_syslog_entry("FAILED");
#endif
      fprintf (stderr,"This is a Virtual Distributed Ethernet (vde) tunnel broker. \n"
                  "This is not a login shell, only vde_plug can be executed\n");
      exit(-1);
}

static void usage(char *progname) {
      fprintf (stderr,"Usage: %s [-p portnum] [-g group] [-m mod] socketname\n\n",progname);
      exit(-1);
}

int main(int argc, char **argv)
{
      static char *sockname=NULL;
      int result;
      register ssize_t nx;
      struct vde_open_args open_args={.port=0,.group=NULL,.mode=0700};

      uname(&me);
      //get the login name
      callerpwd=getpwuid(getuid());

      if (argv[0][0] == '-')
            netusage(); //implies exit
      /* option parsing */
      {
            int c;
            while (1) {
                  int option_index = 0;

                  static struct option long_options[] = {
                        {"sock", 1, 0, 's'},
                        {"vdesock", 1, 0, 's'},
                        {"unix", 1, 0, 's'},
                        {"port", 1, 0, 'p'},
                        {"help",0,0,'h'},
                        {"mod",1,0,'m'},
                        {"group",1,0,'g'},
                        {0, 0, 0, 0}
                  };
                  c = GETOPT_LONG (argc, argv, "hc:p:s:m:g:l",
                              long_options, &option_index);
                  if (c == -1)
                        break;

                  switch (c) {
                        case 'c':
                              if (strcmp(optarg,"vde_plug")==0) {
#ifdef DO_SYSLOG
                                    write_syslog_entry("START");
                                    atexit(write_syslog_close);
#ifdef VDE_IP_LOG
                                    vde_ip_log=1;
#endif
#endif

                              }
                              else
                                    netusage(); //implies exit
                              break;

                        case 'p':
                              open_args.port=atoi(optarg);
                              if (open_args.port <= 0 || open_args.port > 255 )
                                    usage(argv[0]); //implies exit
                              break;

                        case 'h':
                              usage(argv[0]); //implies exit
                              break;

                        case 's':
                              sockname=strdup(optarg);
                              break;

                        case 'm': 
                              sscanf(optarg,"%o",&(open_args.mode));
                              break;

                        case 'g':
                              open_args.group=strdup(optarg);
                              break;

                        case 'l':
#ifdef VDE_IP_LOG
                              write_syslog_entry("START");
                              atexit(write_syslog_close);
                              vde_ip_log=1;
                              break;
#endif

                        default:
                              usage(argv[0]); //implies exit
                  }
            }

            if (optind < argc && sockname==NULL)
                  sockname=argv[optind];
      }
      atexit(cleanup);
      setsighandlers();
      conn=vde_open(sockname,"vde_plug:",&open_args);
      if (conn == NULL)
            exit(1);

      pollv[1].fd=vde_datafd(conn);
      pollv[2].fd=vde_ctlfd(conn);

      for(;;) {
            result=poll(pollv,3,-1);
            if ((pollv[0].revents | pollv[1].revents | pollv[2].revents) & POLLHUP ||
                        pollv[2].revents & POLLIN)
                  break;
            if (pollv[0].revents & POLLIN) {
                  nx=read(STDIN_FILENO,bufin,sizeof(bufin));
                  /* if POLLIN but not data it means that the stream has been
                   * closed at the other end */
                  /*fprintf(stderr,"%s: RECV %d %x %x \n",myname,nx,bufin[0],bufin[1]);*/
                  if (nx==0)
                        break;
                  splitpacket(bufin,nx,conn);
            }
            if (pollv[1].revents & POLLIN) {
                  nx=vde_recv(conn,(char *)(bufin+2),BUFSIZE-2,0);
                  if (nx<0)
                        perror("vde_plug: recvfrom ");
                  else
                  {
                        bufin[0]=nx >> 8;
                        bufin[1]=nx & 0xff;
                        write(STDOUT_FILENO,bufin,nx+2);
                        /*fprintf(stderr,"%s: SENT %d %x %x \n",myname,nx,bufin[0],bufin[1]);*/
                  }
            }
      }

      return(0);
}

Generated by  Doxygen 1.6.0   Back to index