1 /* Copyright 2002 Renzo Davoli
2 * Licensed under the GPL
3 * Modified by Ludovico Gardenghi 2005
15 #include <sys/ioctl.h>
16 #include <sys/socket.h>
20 #include <sys/utsname.h>
21 #include <sys/types.h>
32 #include <arpa/inet.h>
36 #define MIN(X,Y) (((X)<(Y))?(X):(Y))
39 #define SWITCH_MAGIC 0xfeedface
44 enum request_type
{ REQ_NEW_CONTROL
};
49 enum request_type type
;
50 struct sockaddr_un sock
;
51 char description
[MAXDESCR
];
55 #define myname me.nodename
57 static struct passwd
*callerpwd
;
59 static char host
[256];
61 void write_syslog_entry(char *message
)
66 openlog("vde_plug", 0, LOG_USER
);
68 //get the caller IP address
69 //TNX Giordani-Macchia code from vish.c
70 if ((ssh_client
=getenv("SSH_CLIENT"))!=NULL
)
72 for (ip_length
=0;ip_length
<sizeof(host
)&&ssh_client
[ip_length
]!=0&&!isspace(ssh_client
[ip_length
]);ip_length
++);
73 if (ip_length
>=sizeof(host
))
74 ip_length
=sizeof(host
)-1;
75 memcpy(host
,ssh_client
,ip_length
);
79 strcpy(host
,"UNKNOWN_IP_ADDRESS");
80 syslog(LOG_INFO
,"%s: user %s IP %s",message
,callerpwd
->pw_name
,host
);
84 void write_syslog_close()
86 write_syslog_entry("STOP");
95 unsigned char dest
[ETH_ALEN
];
96 unsigned char src
[ETH_ALEN
];
97 unsigned char proto
[2];
102 unsigned char version
;
103 unsigned char filler
[11];
104 unsigned char ip4src
[4];
105 unsigned char ip4dst
[4];
108 unsigned char version
;
109 unsigned char filler
[7];
110 unsigned char ip6src
[16];
111 unsigned char ip6dst
[16];
114 unsigned char priovlan
[2];
118 unsigned char ip4list
[MAX_IP
][4];
119 unsigned char ip6list
[MAX_IP
][16];
120 static unsigned char nulladdr
[16];
122 static int hash4(unsigned char *addr
)
124 return((addr
[0]+2*addr
[1]+3*addr
[2]+5*addr
[3]) % MAX_IP
);
127 static int hash6(unsigned char *addr
)
129 return((addr
[0]+2*addr
[1]+3*addr
[2]+5*addr
[3]+
130 7*addr
[4]+11*addr
[5]+13*addr
[6]+17*addr
[7]+
131 19*addr
[8]+23*addr
[9]+29*addr
[10]+31*addr
[11]+
132 37*addr
[12]+41*addr
[13]+43*addr
[14]+47*addr
[15]) % MAX_IP
);
135 static void vde_ip_check(const unsigned char *buf
,int rnx
)
137 struct header
*ph
=(struct header
*) buf
;
138 register int i
,j
,vlan
=0;
142 pb
=(union body
*)(ph
+1);
143 if (ph
->proto
[0]==0x81 && ph
->proto
[1]==0x00) { /*VLAN*/
144 vlan
=((pb
->vlan
.priovlan
[0] << 8) + pb
->vlan
.priovlan
[1]) & 0xfff;
145 pb
=(union body
*)(((char *)pb
)+4);
147 if (ph
->proto
[0]==0x08 && ph
->proto
[1]==0x00 &&
148 pb
->v4
.version
== 0x45) {
150 i
=hash4(pb
->v4
.ip4src
);
151 j
=(i
+MAX_IP
-1)%MAX_IP
;
153 /* most frequent case first */
154 if (memcmp(pb
->v4
.ip4src
,ip4list
[i
],4) == 0)
156 else if (memcmp(ip4list
[i
],nulladdr
,4) == 0) {
157 memcpy(ip4list
[i
],pb
->v4
.ip4src
,4);
158 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
);
162 syslog(LOG_ERR
,"IPv4 table full. Exiting\n");
169 else if (ph
->proto
[0]==0x86 && ph
->proto
[1]==0xdd &&
170 pb
->v4
.version
== 0x60) {
172 i
=hash6(pb
->v6
.ip6src
);
173 j
=(i
+MAX_IP
-1)%MAX_IP
;
175 /* most frequent case first */
176 if (memcmp(pb
->v6
.ip6src
,ip6list
[i
],16) == 0)
178 else if (memcmp(ip6list
[i
],nulladdr
,16) == 0) {
179 memcpy(ip6list
[i
],pb
->v6
.ip6src
,16);
180 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
);
184 syslog(LOG_ERR
,"IPv6 table full. Exiting\n");
194 unsigned char bufin
[BUFSIZE
];
196 void splitpacket(const unsigned char *buf
,int size
,int fd
, struct sockaddr_un
*pd
)
198 static unsigned char fragment
[BUFSIZE
];
199 static unsigned char *fragp
;
200 static unsigned int rnx
,remaining
;
202 //fprintf(stderr,"%s: splitpacket rnx=%d remaining=%d size=%d\n",myname,rnx,remaining,size);
205 register int amount
=MIN(remaining
,size
);
206 //fprintf(stderr,"%s: fragment amount %d\n",myname,amount);
207 memcpy(fragp
,buf
,amount
);
213 //fprintf(stderr,"%s: delivered defrag %d\n",myname,rnx);
214 //send(fd,fragment,rnx,0);
217 vde_ip_check(buf
,rnx
);
219 sendto(fd
,fragment
,rnx
,0,(struct sockaddr
*) pd
,sizeof(struct sockaddr_un
));
225 rnx
=(buf
[0]<<8)+buf
[1];
227 //fprintf(stderr,"%s %d: packet %d size %d %x %x\n",myname,getpid(),rnx,size,buf[0],buf[1]);
230 fprintf(stderr
,"%s: Packet length error size %d rnx %d\n",myname
,size
,rnx
);
235 //fprintf(stderr,"%s: begin defrag %d\n",myname,rnx);
237 memcpy(fragp
,buf
,size
);
242 //fprintf(stderr,"%s: deliver %d\n",myname,rnx);
243 //send(fd,buf,rnx,0);
246 vde_ip_check(buf
,rnx
);
248 sendto(fd
,buf
,rnx
,0,(struct sockaddr
*) pd
,sizeof(struct sockaddr_un
));
256 static struct sockaddr_un inpath
;
258 static int send_fd(char *name
, int fddata
, struct sockaddr_un
*datasock
, int port
, char *g
, int m
)
261 struct request_v3 req
;
265 static struct sockaddr_un sock
;
267 if((fdctl
= socket(AF_UNIX
, SOCK_STREAM
, 0)) < 0){
276 if(name
[strlen(name
)-1] == ']' && (split
=rindex(name
,'[')) != NULL
) {
280 if (*name
==0) name
=VDESTDSOCK
;
284 sock
.sun_family
= AF_UNIX
;
285 snprintf(sock
.sun_path
, sizeof(sock
.sun_path
), "%s/ctl", name
);
286 if(connect(fdctl
, (struct sockaddr
*) &sock
, sizeof(sock
))){
287 if (name
== VDESTDSOCK
) {
289 snprintf(sock
.sun_path
, sizeof(sock
.sun_path
), "%s/ctl", name
);
290 if(connect(fdctl
, (struct sockaddr
*) &sock
, sizeof(sock
))){
291 snprintf(sock
.sun_path
, sizeof(sock
.sun_path
), "%s", name
);
292 if(connect(fdctl
, (struct sockaddr
*) &sock
, sizeof(sock
))){
300 req
.magic
=SWITCH_MAGIC
;
302 req
.type
=REQ_NEW_CONTROL
+(port
<< 8);
303 req
.sock
.sun_family
=AF_UNIX
;
305 /* First choice, return socket from the switch close to the control dir*/
306 memset(req
.sock
.sun_path
, 0, sizeof(req
.sock
.sun_path
));
307 sprintf(req
.sock
.sun_path
, "%s.%05d-%02d", name
, pid
, 0);
308 if(bind(fddata
, (struct sockaddr
*) &req
.sock
, sizeof(req
.sock
)) < 0){
309 /* if it is not possible -> /tmp */
310 memset(req
.sock
.sun_path
, 0, sizeof(req
.sock
.sun_path
));
311 sprintf(req
.sock
.sun_path
, "/tmp/vde.%05d-%02d", pid
, 0);
312 if(bind(fddata
, (struct sockaddr
*) &req
.sock
, sizeof(req
.sock
)) < 0) {
318 snprintf(req
.description
,MAXDESCR
,"vde_plug user=%s PID=%d %s SOCK=%s",
319 callerpwd
->pw_name
,pid
,getenv("SSH_CLIENT")?getenv("SSH_CLIENT"):"",req
.sock
.sun_path
);
320 memcpy(&inpath
,&req
.sock
,sizeof(req
.sock
));
321 if (send(fdctl
,&req
,sizeof(req
)-MAXDESCR
+strlen(req
.description
),0) < 0) {
326 if (recv(fdctl
,datasock
,sizeof(struct sockaddr_un
),0)<0) {
332 if ((gs
=getgrnam(g
)) == NULL
)
336 chown(inpath
.sun_path
,-1,gid
);
339 chmod(inpath
.sun_path
,m
);
343 static void cleanup(void)
345 unlink(inpath
.sun_path
);
348 static void sig_handler(int sig
)
351 signal(sig
, SIG_DFL
);
355 static void setsighandlers()
357 /* setting signal handlers.
358 * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
359 * ignores all the others signals which could cause termination. */
360 struct { int sig
; const char *name
; int ignore
; } signals
[] = {
361 { SIGHUP
, "SIGHUP", 0 },
362 { SIGINT
, "SIGINT", 0 },
363 { SIGPIPE
, "SIGPIPE", 1 },
364 { SIGALRM
, "SIGALRM", 1 },
365 { SIGTERM
, "SIGTERM", 0 },
366 { SIGUSR1
, "SIGUSR1", 1 },
367 { SIGUSR2
, "SIGUSR2", 1 },
368 { SIGPROF
, "SIGPROF", 1 },
369 { SIGVTALRM
, "SIGVTALRM", 1 },
371 { SIGPOLL
, "SIGPOLL", 1 },
372 { SIGSTKFLT
, "SIGSTKFLT", 1 },
373 { SIGIO
, "SIGIO", 1 },
374 { SIGPWR
, "SIGPWR", 1 },
375 { SIGUNUSED
, "SIGUNUSED", 1 },
378 { SIGXCPU
, "SIGXCPU", 1 },
379 { SIGXFSZ
, "SIGXFSZ", 1 },
385 for(i
= 0; signals
[i
].sig
!= 0; i
++)
386 if(signal(signals
[i
].sig
,
387 signals
[i
].ignore
? SIG_IGN
: sig_handler
) < 0)
388 perror("Setting handler");
391 struct pollfd pollv
[]={{STDIN_FILENO
,POLLIN
|POLLHUP
},{0,POLLIN
|POLLHUP
},{0,POLLIN
|POLLHUP
}};
393 static void netusage() {
395 write_syslog_entry("FAILED");
397 fprintf (stderr
,"This is a Virtual Distributed Ethernet (vde) tunnel broker. \n"
398 "This is not a login shell, only vde_plug can be executed\n");
402 static void usage(char *progname
) {
403 fprintf (stderr
,"Usage: %s [-p portnum] [-g group] [-m mod] socketname\n\n",progname
);
407 int main(int argc
, char **argv
)
410 struct sockaddr_un dataout
;
411 struct sockaddr_un datain
;
412 static char *sockname
=NULL
;
413 unsigned int datainsize
;
423 callerpwd
=getpwuid(getuid());
425 if (argv
[0][0] == '-')
426 netusage(); //implies exit
431 int option_index
= 0;
433 static struct option long_options
[] = {
435 {"vdesock", 1, 0, 's'},
443 c
= GETOPT_LONG (argc
, argv
, "hc:p:s:m:g:l",
444 long_options
, &option_index
);
450 if (strcmp(optarg
,"vde_plug")==0) {
452 write_syslog_entry("START");
453 atexit(write_syslog_close
);
461 netusage(); //implies exit
466 if (port
<= 0 || port
> 255 )
467 usage(argv
[0]); //implies exit
471 usage(argv
[0]); //implies exit
475 sockname
=strdup(optarg
);
479 sscanf(optarg
,"%o",&mode
);
483 group
=strdup(optarg
);
488 write_syslog_entry("START");
489 atexit(write_syslog_close
);
495 usage(argv
[0]); //implies exit
499 if (optind
< argc
&& sockname
==NULL
)
500 sockname
=argv
[optind
];
504 if((fddata
= socket(AF_UNIX
, SOCK_DGRAM
, 0)) < 0){
508 connected_fd
=send_fd(sockname
, fddata
, &dataout
, port
, group
, mode
);
511 pollv
[2].fd
=connected_fd
;
514 result
=poll(pollv
,3,-1);
515 if ((pollv
[0].revents
| pollv
[1].revents
| pollv
[2].revents
) & POLLHUP
||
516 pollv
[2].revents
& POLLIN
)
518 if (pollv
[0].revents
& POLLIN
) {
519 nx
=read(STDIN_FILENO
,bufin
,sizeof(bufin
));
520 /* if POLLIN but not data it means that the stream has been
521 * closed at the other end */
524 splitpacket(bufin
,nx
,fddata
,&dataout
);
526 if (pollv
[1].revents
& POLLIN
) {
527 datainsize
=sizeof(datain
);
528 nx
=recvfrom(fddata
,(char *)(bufin
+2),BUFSIZE
-2,0,(struct sockaddr
*) &datain
, &datainsize
);
530 perror("vde_plug: recvfrom ");
535 write(STDOUT_FILENO
,bufin
,nx
+2);
536 //fprintf(stderr,"%s: SENT %d %x %x \n",myname,nx,bufin[0],bufin[1]);