/* Copyright 2003 Renzo Davoli
* Licensed under the GPL
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#ifndef __FreeBSD__
#include <stdint.h>
#endif
#include <getopt.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>
#ifdef __FreeBSD__
#include <sys/time.h>
#include <netinet/in.h>
#include <string.h>
#endif
#include "vde.h"
#ifdef VDE_IP_LOG
#define DO_SYSLOG
#endif
#ifdef DO_SYSLOG
#include <syslog.h>
#include <sys/types.h>
#include <pwd.h>
#include <ctype.h>
#include <arpa/inet.h>
#endif
#define SWITCH_MAGIC 0xfeedface
#define BUFSIZE 2048
#define ETH_ALEN 6
enum request_type { REQ_NEW_CONTROL };
struct request_v3 {
uint32_t magic;
uint32_t version;
enum request_type type;
struct sockaddr_un sock;
};
struct utsname me;
#define myname me.nodename
#ifdef DO_SYSLOG
static struct passwd *callerpwd;
static char host[256];
#ifdef __FreeBSD__
static char *data_socket = NULL;
static void cleanup()
{
if(data_socket != NULL)
unlink(data_socket);
}
static void sig_handler(int sig)
{
fprintf(stderr, "Caught signal %d, cleaning up and exiting\n", sig);
cleanup(1,NULL);
signal(sig, SIG_DFL);
kill(getpid(), sig);
}
#endif
void write_syslog_entry(char *message)
{
char *ssh_client;
size_t ip_length;
openlog("vde_plug", 0, LOG_USER);
//get the login name
callerpwd=getpwuid(getuid());
//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 packet {
struct {
unsigned char dest[ETH_ALEN];
unsigned char src[ETH_ALEN];
unsigned char proto[2];
} header;
union {
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;
} body;
};
unsigned char ip4list[MAX_IP][4];
unsigned char ip6list[MAX_IP][16];
static unsigned char nulladdr[16];
static int hash4(char *addr)
{
return((addr[0]+2*addr[1]+3*addr[2]+5*addr[3]) % MAX_IP);
}
static int hash6(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[7]+23*addr[8]+29*addr[9]+31*addr[10]+
37*addr[7]+41*addr[8]+43*addr[9]+47*addr[10]) % MAX_IP);
}
static void vde_ip_check(const char *buf,int rnx)
{
struct packet *p=(struct packet *) buf;
register int i,j;
char addr[256];
if (p->header.proto[0]==0x08 && p->header.proto[1]==0x00 &&
p->body.v4.version == 0x45) {
/*v4 */
i=hash4(p->body.v4.ip4src);
j=(i+MAX_IP-1)%MAX_IP;
while (1) {
/* more frequent case first */
if (memcmp(p->body.v4.ip4src,ip4list[i],4) == 0)
break;
else if (memcmp(ip4list[i],nulladdr,4) == 0) {
memcpy(ip4list[i],p->body.v4.ip4src,4);
syslog(LOG_INFO,"user %s Real-IP %s has got VDE-IP4 %s",callerpwd->pw_name,host,inet_ntop(AF_INET,ip4list[i],addr,256));
/*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 (p->header.proto[0]==0x86 && p->header.proto[1]==0xdd &&
p->body.v4.version == 0x60) {
/* v6 */
i=hash6(p->body.v6.ip6src);
j=(i+MAX_IP-1)%MAX_IP;
while (1) {
/* more frequent case first */
if (memcmp(p->body.v6.ip6src,ip6list[i],16) == 0)
break;
else if (memcmp(ip6list[i],nulladdr,16) == 0) {
memcpy(ip6list[i],p->body.v6.ip6src,16);
syslog(LOG_INFO,"user %s Real-IP %s has got VDE-IP6 %s",callerpwd->pw_name,host,inet_ntop(AF_INET6,ip6list[i],addr,256));
/*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
static int send_fd(char *name, int fddata, struct sockaddr_un *datasock, int group)
{
int pid = getpid();
struct request_v3 req;
int fdctl;
struct sockaddr_un sock;
#ifdef __FreeBSD__
struct timeval tv;
#endif
if((fdctl = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){
perror("socket");
exit(1);
}
sock.sun_family = AF_UNIX;
snprintf(sock.sun_path, sizeof(sock.sun_path), "%s", name);
if(connect(fdctl, (struct sockaddr *) &sock, sizeof(sock))){
perror("connect");
exit(1);
}
req.magic=SWITCH_MAGIC;
req.version=3;
req.type=REQ_NEW_CONTROL+((group > 0)?((geteuid()<<8) + group) << 8:0);
req.sock.sun_family=AF_UNIX;
memset(req.sock.sun_path, 0, sizeof(req.sock.sun_path));
#ifdef __FreeBSD__
gettimeofday(&tv, NULL);
snprintf(req.sock.sun_path, sizeof(req.sock.sun_path), "%s.%i.%li",
VDEDATSOCK, pid, tv.tv_usec);
data_socket = strdup(req.sock.sun_path);
#else
sprintf(&req.sock.sun_path[1], "%5d", pid);
#endif
if(bind(fddata, (struct sockaddr *) &req.sock, sizeof(req.sock)) < 0){
perror("bind");
exit(1);
}
if (send(fdctl,&req,sizeof(req),0) < 0) {
perror("send");
exit(1);
}
if (recv(fdctl,datasock,sizeof(struct sockaddr_un),0)<0) {
perror("recv");
exit(1);
}
return fdctl;
}
unsigned char bufin[BUFSIZE];
#define MIN(X,Y) ((X)<(Y))?(X):(Y)
void splitpacket(const unsigned char *buf,int size,int fd, struct sockaddr_un *pd)
{
static unsigned 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);
sendto(fd,fragment,rnx,0,(struct sockaddr *) pd,sizeof(struct sockaddr_un));
rnx=0;
}
}
while (size > 0) {
rnx=(buf[0]<<8)+buf[1];
size-=2;
//fprintf(stderr,"%s: packet %d size %d %x %x\n",myname,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
sendto(fd,buf,rnx,0,(struct sockaddr *) pd,sizeof(struct sockaddr_un));
buf+=rnx;
size-=rnx;
rnx=0;
}
}
}
struct pollfd pollv[]={{STDIN_FILENO,POLLIN|POLLHUP,0},{0,POLLIN|POLLHUP,0}};
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 [-g num] [socketname]\n ( 0 < num < 256 )\n\n",progname);
exit(-1);
}
int main(int argc, char **argv)
{
int fddata;
char *sockname=NULL;
struct sockaddr_un dataout;
struct sockaddr_un datain;
int datainsize;
int result;
int group=0;
int connected_fd;
register ssize_t nx;
#ifdef __FreeBSD__
atexit(cleanup);
#endif
uname(&me);
if (argv[0][0] == '-')
netusage(); //implies exit
sockname=(argc == 2)?argv[argc-1]:VDESTDSOCK;
/* option parsing */
{
int c;
while (1) {
int option_index = 0;
static struct option long_options[] = {
{"group", 1, 0, 's'},
{"sock", 1, 0, 's'},
{"vdesock", 1, 0, 's'},
{"unix", 1, 0, 's'},
{"help",0,0,'h'},
{0, 0, 0, 0}
};
c = getopt_long_only (argc, argv, "c:g:s: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 'g':
group=atoi(optarg);
if (group <= 0 || group > 255 )
usage(argv[0]); //implies exit
break;
case 'h':
usage(argv[0]); //implies exit
break;
case 's':
sockname=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
}
}
sockname=(optind < argc && sockname==NULL)?argv[optind]:VDESTDSOCK;
}
if((fddata = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0){
perror("socket");
exit(1);
}
connected_fd=send_fd(sockname, fddata, &dataout, group);
pollv[1].fd=fddata;
#ifdef __FreeBSD__
if(signal(SIGINT, sig_handler) < 0) {
perror("signal");
}
#endif
for(;;) {
result=poll(pollv,2,-1);
if (pollv[0].revents & POLLHUP || pollv[1].revents & POLLHUP)
break;
if (pollv[0].revents & POLLIN) {
nx=read(STDIN_FILENO,bufin,sizeof(bufin));
splitpacket(bufin,nx,fddata,&dataout);
//sendto(fddata,bufin,nx,0,(struct sockaddr *) &dataout,sizeof(dataout));
}
if (pollv[1].revents & POLLIN) {
datainsize=sizeof(datain);
nx=recvfrom(fddata,bufin+2,BUFSIZE-2,0,(struct sockaddr *) &datain, &datainsize);
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);
}
syntax highlighted by Code2HTML, v. 0.9.1