standard socket changed (in /var/run for root started deamons)
[vde.git] / vde-2 / vde_plug.c
blob12d17dc6635284f697d25ee7f73f65f6d4589c5a
1 /* Copyright 2002 Renzo Davoli
2 * Licensed under the GPL
3 */
5 #include <config.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <signal.h>
9 #include <errno.h>
10 #include <unistd.h>
11 #include <stdint.h>
12 #include <getopt.h>
13 #include <string.h>
14 #include <sys/ioctl.h>
15 #include <sys/socket.h>
16 #include <linux/un.h>
17 #include <sys/uio.h>
18 #include <sys/poll.h>
19 #include <sys/utsname.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <pwd.h>
23 #include <grp.h>
24 #include <vde.h>
25 #ifdef VDE_IP_LOG
26 #define DO_SYSLOG
27 #endif
28 #ifdef DO_SYSLOG
29 #include <syslog.h>
30 #include <ctype.h>
31 #include <arpa/inet.h>
32 #endif
34 #define MIN(X,Y) (((X)<(Y))?(X):(Y))
35 #define SWITCH_MAGIC 0xfeedface
36 #define BUFSIZE 2048
37 #define MAXDESCR 128
38 #define ETH_ALEN 6
40 enum request_type { REQ_NEW_CONTROL };
42 struct request_v3 {
43 uint32_t magic;
44 uint32_t version;
45 enum request_type type;
46 struct sockaddr_un sock;
47 char description[MAXDESCR];
50 struct utsname me;
51 #define myname me.nodename
53 static struct passwd *callerpwd;
54 #ifdef DO_SYSLOG
55 static char host[256];
57 void write_syslog_entry(char *message)
59 char *ssh_client;
60 size_t ip_length;
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);
72 host[ip_length]=0;
74 else
75 strcpy(host,"UNKNOWN_IP_ADDRESS");
76 syslog(LOG_INFO,"%s: user %s IP %s",message,callerpwd->pw_name,host);
77 closelog();
80 void write_syslog_close()
82 write_syslog_entry("STOP");
84 #endif
86 #ifdef VDE_IP_LOG
87 #define MAX_IP 256
88 int vde_ip_log;
90 struct header {
91 unsigned char dest[ETH_ALEN];
92 unsigned char src[ETH_ALEN];
93 unsigned char proto[2];
96 union body {
97 struct {
98 unsigned char version;
99 unsigned char filler[11];
100 unsigned char ip4src[4];
101 unsigned char ip4dst[4];
102 } v4;
103 struct {
104 unsigned char version;
105 unsigned char filler[7];
106 unsigned char ip6src[16];
107 unsigned char ip6dst[16];
108 } v6;
109 struct {
110 unsigned char priovlan[2];
111 } vlan;
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;
135 char addr[256];
136 union body *pb;
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) {
145 /*v4 */
146 i=hash4(pb->v4.ip4src);
147 j=(i+MAX_IP-1)%MAX_IP;
148 while (1) {
149 /* most frequent case first */
150 if (memcmp(pb->v4.ip4src,ip4list[i],4) == 0)
151 break;
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);
155 /*new ipv4*/
156 break;
157 } else if (i==j) {
158 syslog(LOG_ERR,"IPv4 table full. Exiting\n");
159 /*full table*/
160 exit(-1);
161 } else
162 i= (i+1)%MAX_IP;
165 else if (ph->proto[0]==0x86 && ph->proto[1]==0xdd &&
166 pb->v4.version == 0x60) {
167 /* v6 */
168 i=hash6(pb->v6.ip6src);
169 j=(i+MAX_IP-1)%MAX_IP;
170 while (1) {
171 /* most frequent case first */
172 if (memcmp(pb->v6.ip6src,ip6list[i],16) == 0)
173 break;
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);
177 /*new ipv6*/
178 break;
179 } else if (i==j) {
180 syslog(LOG_ERR,"IPv6 table full. Exiting\n");
181 /*full table*/
182 exit(-1);
183 } else
184 i= (i+1)%MAX_IP;
188 #endif
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);
199 if (size==0) return;
200 if (rnx>0) {
201 register int amount=MIN(remaining,size);
202 //fprintf(stderr,"%s: fragment amount %d\n",myname,amount);
203 memcpy(fragp,buf,amount);
204 remaining-=amount;
205 fragp+=amount;
206 buf+=amount;
207 size-=amount;
208 if (remaining==0) {
209 //fprintf(stderr,"%s: delivered defrag %d\n",myname,rnx);
210 //send(fd,fragment,rnx,0);
211 #ifdef VDE_IP_LOG
212 if (vde_ip_log)
213 vde_ip_check(buf,rnx);
214 #endif
215 sendto(fd,fragment,rnx,0,(struct sockaddr *) pd,sizeof(struct sockaddr_un));
217 rnx=0;
220 while (size > 0) {
221 rnx=(buf[0]<<8)+buf[1];
222 size-=2;
223 //fprintf(stderr,"%s %d: packet %d size %d %x %x\n",myname,getpid(),rnx,size,buf[0],buf[1]);
224 buf+=2;
225 if (rnx>1521) {
226 fprintf(stderr,"%s: Packet length error size %d rnx %d\n",myname,size,rnx);
227 rnx=0;
228 return;
230 if (rnx > size) {
231 //fprintf(stderr,"%s: begin defrag %d\n",myname,rnx);
232 fragp=fragment;
233 memcpy(fragp,buf,size);
234 remaining=rnx-size;
235 fragp+=size;
236 size=0;
237 } else {
238 //fprintf(stderr,"%s: deliver %d\n",myname,rnx);
239 //send(fd,buf,rnx,0);
240 #ifdef VDE_IP_LOG
241 if (vde_ip_log)
242 vde_ip_check(buf,rnx);
243 #endif
244 sendto(fd,buf,rnx,0,(struct sockaddr *) pd,sizeof(struct sockaddr_un));
245 buf+=rnx;
246 size-=rnx;
247 rnx=0;
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)
256 int pid = getpid();
257 struct request_v3 req;
258 int fdctl;
259 int gid;
260 struct group *gs;
261 static struct sockaddr_un sock;
263 if((fdctl = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){
264 perror("socket");
265 exit(1);
268 if (name == NULL)
269 name=VDESTDSOCK;
270 else {
271 char *split;
272 if(name[strlen(name)-1] == ']' && (split=rindex(name,'[')) != NULL) {
273 *split=0;
274 split++;
275 port=atoi(split);
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) {
284 name=VDETMPSOCK;
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))){
289 perror("connect");
290 exit(1);
296 req.magic=SWITCH_MAGIC;
297 req.version=3;
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) {
309 perror("bind");
310 exit(1);
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) {
318 perror("send");
319 exit(1);
322 if (recv(fdctl,datasock,sizeof(struct sockaddr_un),0)<0) {
323 perror("recv");
324 exit(1);
327 if (g) {
328 if ((gs=getgrnam(g)) == NULL)
329 gid=atoi(g);
330 else
331 gid=gs->gr_gid;
332 chown(inpath.sun_path,-1,gid);
334 if (m>=0)
335 chmod(inpath.sun_path,m);
336 return fdctl;
339 static void cleanup(void)
341 unlink(inpath.sun_path);
344 static void sig_handler(int sig)
346 cleanup();
347 signal(sig, SIG_DFL);
348 kill(getpid(), sig);
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 },
371 { 0, NULL, 0 }
374 int i;
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() {
384 #ifdef DO_SYSLOG
385 write_syslog_entry("FAILED");
386 #endif
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");
389 exit(-1);
392 static void usage(char *progname) {
393 fprintf (stderr,"Usage: %s [-p portnum] [-g group] [-m mod] socketname\n\n",progname);
394 exit(-1);
397 int main(int argc, char **argv)
399 int fddata;
400 struct sockaddr_un dataout;
401 struct sockaddr_un datain;
402 static char *sockname=NULL;
403 unsigned int datainsize;
404 int result;
405 int port=0;
406 int connected_fd;
407 register ssize_t nx;
408 char *group=NULL;
409 int mode=0700;
411 uname(&me);
412 //get the login name
413 callerpwd=getpwuid(getuid());
415 if (argv[0][0] == '-')
416 netusage(); //implies exit
417 /* option parsing */
419 int c;
420 while (1) {
421 int option_index = 0;
423 static struct option long_options[] = {
424 {"sock", 1, 0, 's'},
425 {"vdesock", 1, 0, 's'},
426 {"unix", 1, 0, 's'},
427 {"port", 1, 0, 'p'},
428 {"help",0,0,'h'},
429 {"mod",1,0,'m'},
430 {"group",1,0,'g'},
431 {0, 0, 0, 0}
433 c = getopt_long_only (argc, argv, "hc:p:s:m:g:l",
434 long_options, &option_index);
435 if (c == -1)
436 break;
438 switch (c) {
439 case 'c':
440 if (strcmp(optarg,"vde_plug")==0) {
441 #ifdef DO_SYSLOG
442 write_syslog_entry("START");
443 atexit(write_syslog_close);
444 #ifdef VDE_IP_LOG
445 vde_ip_log=1;
446 #endif
447 #endif
450 else
451 netusage(); //implies exit
452 break;
454 case 'p':
455 port=atoi(optarg);
456 if (port <= 0 || port > 255 )
457 usage(argv[0]); //implies exit
458 break;
460 case 'h':
461 usage(argv[0]); //implies exit
462 break;
464 case 's':
465 sockname=strdup(optarg);
466 break;
468 case 'm':
469 sscanf(optarg,"%o",&mode);
470 break;
472 case 'g':
473 group=strdup(optarg);
474 break;
476 case 'l':
477 #ifdef VDE_IP_LOG
478 write_syslog_entry("START");
479 atexit(write_syslog_close);
480 vde_ip_log=1;
481 break;
482 #endif
484 default:
485 usage(argv[0]); //implies exit
489 if (optind < argc && sockname==NULL)
490 sockname=argv[optind];
492 atexit(cleanup);
493 setsighandlers();
494 if((fddata = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0){
495 perror("socket");
496 exit(1);
498 connected_fd=send_fd(sockname, fddata, &dataout, port, group, mode);
500 pollv[1].fd=fddata;
501 pollv[2].fd=connected_fd;
503 for(;;) {
504 result=poll(pollv,3,-1);
505 if ((pollv[0].revents | pollv[1].revents | pollv[2].revents) & POLLHUP ||
506 pollv[2].revents & POLLIN)
507 break;
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 */
512 if (nx==0)
513 break;
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);
519 if (nx<0)
520 perror("vde_plug: recvfrom ");
521 else
523 bufin[0]=nx >> 8;
524 bufin[1]=nx & 0xff;
525 write(STDOUT_FILENO,bufin,nx+2);
526 //fprintf(stderr,"%s: SENT %d %x %x \n",myname,nx,bufin[0],bufin[1]);
531 return(0);