- added support for Mac OSX (10.3 Panther and 10.4 Tiger). For tuntap support
[vde.git] / vde-2 / vde_plug.c
blob29beb2b4a95b205dd03d3ba98572e6d114cba9b6
1 /* Copyright 2002 Renzo Davoli
2 * Licensed under the GPL
3 * Modified by Ludovico Gardenghi 2005
4 */
6 #include <config.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <signal.h>
10 #include <errno.h>
11 #include <unistd.h>
12 #include <stdint.h>
13 #include <getopt.h>
14 #include <string.h>
15 #include <sys/ioctl.h>
16 #include <sys/socket.h>
17 #include <sys/un.h>
18 #include <sys/uio.h>
19 #include <sys/poll.h>
20 #include <sys/utsname.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <pwd.h>
24 #include <grp.h>
25 #include <vde.h>
26 #ifdef VDE_IP_LOG
27 #define DO_SYSLOG
28 #endif
29 #ifdef DO_SYSLOG
30 #include <syslog.h>
31 #include <ctype.h>
32 #include <arpa/inet.h>
33 #endif
35 #ifndef MIN
36 #define MIN(X,Y) (((X)<(Y))?(X):(Y))
37 #endif
39 #define SWITCH_MAGIC 0xfeedface
40 #define BUFSIZE 2048
41 #define MAXDESCR 128
42 #define ETH_ALEN 6
44 enum request_type { REQ_NEW_CONTROL };
46 struct request_v3 {
47 uint32_t magic;
48 uint32_t version;
49 enum request_type type;
50 struct sockaddr_un sock;
51 char description[MAXDESCR];
54 struct utsname me;
55 #define myname me.nodename
57 static struct passwd *callerpwd;
58 #ifdef DO_SYSLOG
59 static char host[256];
61 void write_syslog_entry(char *message)
63 char *ssh_client;
64 size_t ip_length;
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);
76 host[ip_length]=0;
78 else
79 strcpy(host,"UNKNOWN_IP_ADDRESS");
80 syslog(LOG_INFO,"%s: user %s IP %s",message,callerpwd->pw_name,host);
81 closelog();
84 void write_syslog_close()
86 write_syslog_entry("STOP");
88 #endif
90 #ifdef VDE_IP_LOG
91 #define MAX_IP 256
92 int vde_ip_log;
94 struct header {
95 unsigned char dest[ETH_ALEN];
96 unsigned char src[ETH_ALEN];
97 unsigned char proto[2];
100 union body {
101 struct {
102 unsigned char version;
103 unsigned char filler[11];
104 unsigned char ip4src[4];
105 unsigned char ip4dst[4];
106 } v4;
107 struct {
108 unsigned char version;
109 unsigned char filler[7];
110 unsigned char ip6src[16];
111 unsigned char ip6dst[16];
112 } v6;
113 struct {
114 unsigned char priovlan[2];
115 } vlan;
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;
139 char addr[256];
140 union body *pb;
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) {
149 /*v4 */
150 i=hash4(pb->v4.ip4src);
151 j=(i+MAX_IP-1)%MAX_IP;
152 while (1) {
153 /* most frequent case first */
154 if (memcmp(pb->v4.ip4src,ip4list[i],4) == 0)
155 break;
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);
159 /*new ipv4*/
160 break;
161 } else if (i==j) {
162 syslog(LOG_ERR,"IPv4 table full. Exiting\n");
163 /*full table*/
164 exit(-1);
165 } else
166 i= (i+1)%MAX_IP;
169 else if (ph->proto[0]==0x86 && ph->proto[1]==0xdd &&
170 pb->v4.version == 0x60) {
171 /* v6 */
172 i=hash6(pb->v6.ip6src);
173 j=(i+MAX_IP-1)%MAX_IP;
174 while (1) {
175 /* most frequent case first */
176 if (memcmp(pb->v6.ip6src,ip6list[i],16) == 0)
177 break;
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);
181 /*new ipv6*/
182 break;
183 } else if (i==j) {
184 syslog(LOG_ERR,"IPv6 table full. Exiting\n");
185 /*full table*/
186 exit(-1);
187 } else
188 i= (i+1)%MAX_IP;
192 #endif
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);
203 if (size==0) return;
204 if (rnx>0) {
205 register int amount=MIN(remaining,size);
206 //fprintf(stderr,"%s: fragment amount %d\n",myname,amount);
207 memcpy(fragp,buf,amount);
208 remaining-=amount;
209 fragp+=amount;
210 buf+=amount;
211 size-=amount;
212 if (remaining==0) {
213 //fprintf(stderr,"%s: delivered defrag %d\n",myname,rnx);
214 //send(fd,fragment,rnx,0);
215 #ifdef VDE_IP_LOG
216 if (vde_ip_log)
217 vde_ip_check(buf,rnx);
218 #endif
219 sendto(fd,fragment,rnx,0,(struct sockaddr *) pd,sizeof(struct sockaddr_un));
221 rnx=0;
224 while (size > 0) {
225 rnx=(buf[0]<<8)+buf[1];
226 size-=2;
227 //fprintf(stderr,"%s %d: packet %d size %d %x %x\n",myname,getpid(),rnx,size,buf[0],buf[1]);
228 buf+=2;
229 if (rnx>1521) {
230 fprintf(stderr,"%s: Packet length error size %d rnx %d\n",myname,size,rnx);
231 rnx=0;
232 return;
234 if (rnx > size) {
235 //fprintf(stderr,"%s: begin defrag %d\n",myname,rnx);
236 fragp=fragment;
237 memcpy(fragp,buf,size);
238 remaining=rnx-size;
239 fragp+=size;
240 size=0;
241 } else {
242 //fprintf(stderr,"%s: deliver %d\n",myname,rnx);
243 //send(fd,buf,rnx,0);
244 #ifdef VDE_IP_LOG
245 if (vde_ip_log)
246 vde_ip_check(buf,rnx);
247 #endif
248 sendto(fd,buf,rnx,0,(struct sockaddr *) pd,sizeof(struct sockaddr_un));
249 buf+=rnx;
250 size-=rnx;
251 rnx=0;
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)
260 int pid = getpid();
261 struct request_v3 req;
262 int fdctl;
263 int gid;
264 struct group *gs;
265 static struct sockaddr_un sock;
267 if((fdctl = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){
268 perror("socket");
269 exit(1);
272 if (name == NULL)
273 name=VDESTDSOCK;
274 else {
275 char *split;
276 if(name[strlen(name)-1] == ']' && (split=rindex(name,'[')) != NULL) {
277 *split=0;
278 split++;
279 port=atoi(split);
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) {
288 name=VDETMPSOCK;
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))){
293 perror("connect");
294 exit(1);
300 req.magic=SWITCH_MAGIC;
301 req.version=3;
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) {
313 perror("bind");
314 exit(1);
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) {
322 perror("send");
323 exit(1);
326 if (recv(fdctl,datasock,sizeof(struct sockaddr_un),0)<0) {
327 perror("recv");
328 exit(1);
331 if (g) {
332 if ((gs=getgrnam(g)) == NULL)
333 gid=atoi(g);
334 else
335 gid=gs->gr_gid;
336 chown(inpath.sun_path,-1,gid);
338 if (m>=0)
339 chmod(inpath.sun_path,m);
340 return fdctl;
343 static void cleanup(void)
345 unlink(inpath.sun_path);
348 static void sig_handler(int sig)
350 cleanup();
351 signal(sig, SIG_DFL);
352 kill(getpid(), sig);
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 },
370 #ifdef VDE_LINUX
371 { SIGPOLL, "SIGPOLL", 1 },
372 { SIGSTKFLT, "SIGSTKFLT", 1 },
373 { SIGIO, "SIGIO", 1 },
374 { SIGPWR, "SIGPWR", 1 },
375 { SIGUNUSED, "SIGUNUSED", 1 },
376 #endif
377 #ifdef VDE_DARWIN
378 { SIGXCPU, "SIGXCPU", 1 },
379 { SIGXFSZ, "SIGXFSZ", 1 },
380 #endif
381 { 0, NULL, 0 }
384 int i;
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() {
394 #ifdef DO_SYSLOG
395 write_syslog_entry("FAILED");
396 #endif
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");
399 exit(-1);
402 static void usage(char *progname) {
403 fprintf (stderr,"Usage: %s [-p portnum] [-g group] [-m mod] socketname\n\n",progname);
404 exit(-1);
407 int main(int argc, char **argv)
409 int fddata;
410 struct sockaddr_un dataout;
411 struct sockaddr_un datain;
412 static char *sockname=NULL;
413 unsigned int datainsize;
414 int result;
415 int port=0;
416 int connected_fd;
417 register ssize_t nx;
418 char *group=NULL;
419 int mode=0700;
421 uname(&me);
422 //get the login name
423 callerpwd=getpwuid(getuid());
425 if (argv[0][0] == '-')
426 netusage(); //implies exit
427 /* option parsing */
429 int c;
430 while (1) {
431 int option_index = 0;
433 static struct option long_options[] = {
434 {"sock", 1, 0, 's'},
435 {"vdesock", 1, 0, 's'},
436 {"unix", 1, 0, 's'},
437 {"port", 1, 0, 'p'},
438 {"help",0,0,'h'},
439 {"mod",1,0,'m'},
440 {"group",1,0,'g'},
441 {0, 0, 0, 0}
443 c = GETOPT_LONG (argc, argv, "hc:p:s:m:g:l",
444 long_options, &option_index);
445 if (c == -1)
446 break;
448 switch (c) {
449 case 'c':
450 if (strcmp(optarg,"vde_plug")==0) {
451 #ifdef DO_SYSLOG
452 write_syslog_entry("START");
453 atexit(write_syslog_close);
454 #ifdef VDE_IP_LOG
455 vde_ip_log=1;
456 #endif
457 #endif
460 else
461 netusage(); //implies exit
462 break;
464 case 'p':
465 port=atoi(optarg);
466 if (port <= 0 || port > 255 )
467 usage(argv[0]); //implies exit
468 break;
470 case 'h':
471 usage(argv[0]); //implies exit
472 break;
474 case 's':
475 sockname=strdup(optarg);
476 break;
478 case 'm':
479 sscanf(optarg,"%o",&mode);
480 break;
482 case 'g':
483 group=strdup(optarg);
484 break;
486 case 'l':
487 #ifdef VDE_IP_LOG
488 write_syslog_entry("START");
489 atexit(write_syslog_close);
490 vde_ip_log=1;
491 break;
492 #endif
494 default:
495 usage(argv[0]); //implies exit
499 if (optind < argc && sockname==NULL)
500 sockname=argv[optind];
502 atexit(cleanup);
503 setsighandlers();
504 if((fddata = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0){
505 perror("socket");
506 exit(1);
508 connected_fd=send_fd(sockname, fddata, &dataout, port, group, mode);
510 pollv[1].fd=fddata;
511 pollv[2].fd=connected_fd;
513 for(;;) {
514 result=poll(pollv,3,-1);
515 if ((pollv[0].revents | pollv[1].revents | pollv[2].revents) & POLLHUP ||
516 pollv[2].revents & POLLIN)
517 break;
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 */
522 if (nx==0)
523 break;
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);
529 if (nx<0)
530 perror("vde_plug: recvfrom ");
531 else
533 bufin[0]=nx >> 8;
534 bufin[1]=nx & 0xff;
535 write(STDOUT_FILENO,bufin,nx+2);
536 //fprintf(stderr,"%s: SENT %d %x %x \n",myname,nx,bufin[0],bufin[1]);
541 return(0);