1 /* Copyright 2002 Renzo Davoli
2 * Licensed under the GPL
14 #include <sys/ioctl.h>
15 #include <sys/socket.h>
19 #include <sys/utsname.h>
20 #include <sys/types.h>
31 #include <arpa/inet.h>
34 #define MIN(X,Y) (((X)<(Y))?(X):(Y))
35 #define SWITCH_MAGIC 0xfeedface
40 enum request_type
{ REQ_NEW_CONTROL
};
45 enum request_type type
;
46 struct sockaddr_un sock
;
47 char description
[MAXDESCR
];
51 #define myname me.nodename
53 static struct passwd
*callerpwd
;
55 static char host
[256];
57 void write_syslog_entry(char *message
)
62 openlog("vde_plug", 0, LOG_USER
);
64 //get the caller IP address
65 //TNX Giordani-Macchia code from vish.c
66 if ((ssh_client
=getenv("SSH_CLIENT"))!=NULL
)
68 for (ip_length
=0;ip_length
<sizeof(host
)&&ssh_client
[ip_length
]!=0&&!isspace(ssh_client
[ip_length
]);ip_length
++);
69 if (ip_length
>=sizeof(host
))
70 ip_length
=sizeof(host
)-1;
71 memcpy(host
,ssh_client
,ip_length
);
75 strcpy(host
,"UNKNOWN_IP_ADDRESS");
76 syslog(LOG_INFO
,"%s: user %s IP %s",message
,callerpwd
->pw_name
,host
);
80 void write_syslog_close()
82 write_syslog_entry("STOP");
91 unsigned char dest
[ETH_ALEN
];
92 unsigned char src
[ETH_ALEN
];
93 unsigned char proto
[2];
98 unsigned char version
;
99 unsigned char filler
[11];
100 unsigned char ip4src
[4];
101 unsigned char ip4dst
[4];
104 unsigned char version
;
105 unsigned char filler
[7];
106 unsigned char ip6src
[16];
107 unsigned char ip6dst
[16];
110 unsigned char priovlan
[2];
114 unsigned char ip4list
[MAX_IP
][4];
115 unsigned char ip6list
[MAX_IP
][16];
116 static unsigned char nulladdr
[16];
118 static int hash4(unsigned char *addr
)
120 return((addr
[0]+2*addr
[1]+3*addr
[2]+5*addr
[3]) % MAX_IP
);
123 static int hash6(unsigned char *addr
)
125 return((addr
[0]+2*addr
[1]+3*addr
[2]+5*addr
[3]+
126 7*addr
[4]+11*addr
[5]+13*addr
[6]+17*addr
[7]+
127 19*addr
[8]+23*addr
[9]+29*addr
[10]+31*addr
[11]+
128 37*addr
[12]+41*addr
[13]+43*addr
[14]+47*addr
[15]) % MAX_IP
);
131 static void vde_ip_check(const unsigned char *buf
,int rnx
)
133 struct header
*ph
=(struct header
*) buf
;
134 register int i
,j
,vlan
=0;
138 pb
=(union body
*)(ph
+1);
139 if (ph
->proto
[0]==0x81 && ph
->proto
[1]==0x00) { /*VLAN*/
140 vlan
=((pb
->vlan
.priovlan
[0] << 8) + pb
->vlan
.priovlan
[1]) & 0xfff;
141 pb
=(union body
*)(((char *)pb
)+4);
143 if (ph
->proto
[0]==0x08 && ph
->proto
[1]==0x00 &&
144 pb
->v4
.version
== 0x45) {
146 i
=hash4(pb
->v4
.ip4src
);
147 j
=(i
+MAX_IP
-1)%MAX_IP
;
149 /* most frequent case first */
150 if (memcmp(pb
->v4
.ip4src
,ip4list
[i
],4) == 0)
152 else if (memcmp(ip4list
[i
],nulladdr
,4) == 0) {
153 memcpy(ip4list
[i
],pb
->v4
.ip4src
,4);
154 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
);
158 syslog(LOG_ERR
,"IPv4 table full. Exiting\n");
165 else if (ph
->proto
[0]==0x86 && ph
->proto
[1]==0xdd &&
166 pb
->v4
.version
== 0x60) {
168 i
=hash6(pb
->v6
.ip6src
);
169 j
=(i
+MAX_IP
-1)%MAX_IP
;
171 /* most frequent case first */
172 if (memcmp(pb
->v6
.ip6src
,ip6list
[i
],16) == 0)
174 else if (memcmp(ip6list
[i
],nulladdr
,16) == 0) {
175 memcpy(ip6list
[i
],pb
->v6
.ip6src
,16);
176 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
);
180 syslog(LOG_ERR
,"IPv6 table full. Exiting\n");
190 unsigned char bufin
[BUFSIZE
];
192 void splitpacket(const unsigned char *buf
,int size
,int fd
, struct sockaddr_un
*pd
)
194 static unsigned char fragment
[BUFSIZE
];
195 static unsigned char *fragp
;
196 static unsigned int rnx
,remaining
;
198 //fprintf(stderr,"%s: splitpacket rnx=%d remaining=%d size=%d\n",myname,rnx,remaining,size);
201 register int amount
=MIN(remaining
,size
);
202 //fprintf(stderr,"%s: fragment amount %d\n",myname,amount);
203 memcpy(fragp
,buf
,amount
);
209 //fprintf(stderr,"%s: delivered defrag %d\n",myname,rnx);
210 //send(fd,fragment,rnx,0);
213 vde_ip_check(buf
,rnx
);
215 sendto(fd
,fragment
,rnx
,0,(struct sockaddr
*) pd
,sizeof(struct sockaddr_un
));
221 rnx
=(buf
[0]<<8)+buf
[1];
223 //fprintf(stderr,"%s %d: packet %d size %d %x %x\n",myname,getpid(),rnx,size,buf[0],buf[1]);
226 fprintf(stderr
,"%s: Packet length error size %d rnx %d\n",myname
,size
,rnx
);
231 //fprintf(stderr,"%s: begin defrag %d\n",myname,rnx);
233 memcpy(fragp
,buf
,size
);
238 //fprintf(stderr,"%s: deliver %d\n",myname,rnx);
239 //send(fd,buf,rnx,0);
242 vde_ip_check(buf
,rnx
);
244 sendto(fd
,buf
,rnx
,0,(struct sockaddr
*) pd
,sizeof(struct sockaddr_un
));
252 static struct sockaddr_un inpath
;
254 static int send_fd(char *name
, int fddata
, struct sockaddr_un
*datasock
, int port
, char *g
, int m
)
257 struct request_v3 req
;
261 static struct sockaddr_un sock
;
263 if((fdctl
= socket(AF_UNIX
, SOCK_STREAM
, 0)) < 0){
272 if(name
[strlen(name
)-1] == ']' && (split
=rindex(name
,'[')) != NULL
) {
276 if (*name
==0) name
=VDESTDSOCK
;
280 sock
.sun_family
= AF_UNIX
;
281 snprintf(sock
.sun_path
, sizeof(sock
.sun_path
), "%s/ctl", name
);
282 if(connect(fdctl
, (struct sockaddr
*) &sock
, sizeof(sock
))){
283 if (name
== VDESTDSOCK
) {
285 snprintf(sock
.sun_path
, sizeof(sock
.sun_path
), "%s/ctl", name
);
286 if(connect(fdctl
, (struct sockaddr
*) &sock
, sizeof(sock
))){
287 snprintf(sock
.sun_path
, sizeof(sock
.sun_path
), "%s", name
);
288 if(connect(fdctl
, (struct sockaddr
*) &sock
, sizeof(sock
))){
296 req
.magic
=SWITCH_MAGIC
;
298 req
.type
=REQ_NEW_CONTROL
+(port
<< 8);
299 req
.sock
.sun_family
=AF_UNIX
;
301 /* First choice, return socket from the switch close to the control dir*/
302 memset(req
.sock
.sun_path
, 0, sizeof(req
.sock
.sun_path
));
303 sprintf(req
.sock
.sun_path
, "%s.%05d-%02d", name
, pid
, 0);
304 if(bind(fddata
, (struct sockaddr
*) &req
.sock
, sizeof(req
.sock
)) < 0){
305 /* if it is not possible -> /tmp */
306 memset(req
.sock
.sun_path
, 0, sizeof(req
.sock
.sun_path
));
307 sprintf(req
.sock
.sun_path
, "/tmp/vde.%05d-%02d", pid
, 0);
308 if(bind(fddata
, (struct sockaddr
*) &req
.sock
, sizeof(req
.sock
)) < 0) {
314 snprintf(req
.description
,MAXDESCR
,"vde_plug user=%s PID=%d %s SOCK=%s",
315 callerpwd
->pw_name
,pid
,getenv("SSH_CLIENT")?getenv("SSH_CLIENT"):"",req
.sock
.sun_path
);
316 memcpy(&inpath
,&req
.sock
,sizeof(req
.sock
));
317 if (send(fdctl
,&req
,sizeof(req
)-MAXDESCR
+strlen(req
.description
),0) < 0) {
322 if (recv(fdctl
,datasock
,sizeof(struct sockaddr_un
),0)<0) {
328 if ((gs
=getgrnam(g
)) == NULL
)
332 chown(inpath
.sun_path
,-1,gid
);
335 chmod(inpath
.sun_path
,m
);
339 static void cleanup(void)
341 unlink(inpath
.sun_path
);
344 static void sig_handler(int sig
)
347 signal(sig
, SIG_DFL
);
351 static void setsighandlers()
353 /* setting signal handlers.
354 * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
355 * ignores all the others signals which could cause termination. */
356 struct { int sig
; const char *name
; int ignore
; } signals
[] = {
357 { SIGHUP
, "SIGHUP", 0 },
358 { SIGINT
, "SIGINT", 0 },
359 { SIGPIPE
, "SIGPIPE", 1 },
360 { SIGALRM
, "SIGALRM", 1 },
361 { SIGTERM
, "SIGTERM", 0 },
362 { SIGUSR1
, "SIGUSR1", 1 },
363 { SIGUSR2
, "SIGUSR2", 1 },
364 { SIGPOLL
, "SIGPOLL", 1 },
365 { SIGPROF
, "SIGPROF", 1 },
366 { SIGVTALRM
, "SIGVTALRM", 1 },
367 { SIGSTKFLT
, "SIGSTKFLT", 1 },
368 { SIGIO
, "SIGIO", 1 },
369 { SIGPWR
, "SIGPWR", 1 },
370 { SIGUNUSED
, "SIGUNUSED", 1 },
375 for(i
= 0; signals
[i
].sig
!= 0; i
++)
376 if(signal(signals
[i
].sig
,
377 signals
[i
].ignore
? SIG_IGN
: sig_handler
) < 0)
378 perror("Setting handler");
381 struct pollfd pollv
[]={{STDIN_FILENO
,POLLIN
|POLLHUP
},{0,POLLIN
|POLLHUP
},{0,POLLIN
|POLLHUP
}};
383 static void netusage() {
385 write_syslog_entry("FAILED");
387 fprintf (stderr
,"This is a Virtual Distributed Ethernet (vde) tunnel broker. \n"
388 "This is not a login shell, only vde_plug can be executed\n");
392 static void usage(char *progname
) {
393 fprintf (stderr
,"Usage: %s [-p portnum] [-g group] [-m mod] socketname\n\n",progname
);
397 int main(int argc
, char **argv
)
400 struct sockaddr_un dataout
;
401 struct sockaddr_un datain
;
402 static char *sockname
=NULL
;
403 unsigned int datainsize
;
413 callerpwd
=getpwuid(getuid());
415 if (argv
[0][0] == '-')
416 netusage(); //implies exit
421 int option_index
= 0;
423 static struct option long_options
[] = {
425 {"vdesock", 1, 0, 's'},
433 c
= getopt_long_only (argc
, argv
, "hc:p:s:m:g:l",
434 long_options
, &option_index
);
440 if (strcmp(optarg
,"vde_plug")==0) {
442 write_syslog_entry("START");
443 atexit(write_syslog_close
);
451 netusage(); //implies exit
456 if (port
<= 0 || port
> 255 )
457 usage(argv
[0]); //implies exit
461 usage(argv
[0]); //implies exit
465 sockname
=strdup(optarg
);
469 sscanf(optarg
,"%o",&mode
);
473 group
=strdup(optarg
);
478 write_syslog_entry("START");
479 atexit(write_syslog_close
);
485 usage(argv
[0]); //implies exit
489 if (optind
< argc
&& sockname
==NULL
)
490 sockname
=argv
[optind
];
494 if((fddata
= socket(AF_UNIX
, SOCK_DGRAM
, 0)) < 0){
498 connected_fd
=send_fd(sockname
, fddata
, &dataout
, port
, group
, mode
);
501 pollv
[2].fd
=connected_fd
;
504 result
=poll(pollv
,3,-1);
505 if ((pollv
[0].revents
| pollv
[1].revents
| pollv
[2].revents
) & POLLHUP
||
506 pollv
[2].revents
& POLLIN
)
508 if (pollv
[0].revents
& POLLIN
) {
509 nx
=read(STDIN_FILENO
,bufin
,sizeof(bufin
));
510 /* if POLLIN but not data it means that the stream has been
511 * closed at the other end */
514 splitpacket(bufin
,nx
,fddata
,&dataout
);
516 if (pollv
[1].revents
& POLLIN
) {
517 datainsize
=sizeof(datain
);
518 nx
=recvfrom(fddata
,(char *)(bufin
+2),BUFSIZE
-2,0,(struct sockaddr
*) &datain
, &datainsize
);
520 perror("vde_plug: recvfrom ");
525 write(STDOUT_FILENO
,bufin
,nx
+2);
526 //fprintf(stderr,"%s: SENT %d %x %x \n",myname,nx,bufin[0],bufin[1]);