slirpvde. Experimental feature added: port redirect
[vde.git] / vde-2 / slirpvde / slirpvde.c
blobdd65ff52c7253c5bc9b0fadbdb2c710369862a22
1 /* Copyright 2003 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 <string.h>
11 #include <syslog.h>
12 #include <errno.h>
13 #include <unistd.h>
14 #include <stdint.h>
15 #include <libgen.h>
16 #include <sys/ioctl.h>
17 #include <sys/socket.h>
18 #include <sys/un.h>
19 #include <sys/uio.h>
20 #include <sys/poll.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <libslirp.h>
24 #include <pwd.h>
25 #include <grp.h>
26 #include <getopt.h>
27 #include <stdarg.h>
28 #include <fcntl.h>
29 #include <vde.h>
31 #define SWITCH_MAGIC 0xfeedface
32 #define BUFSIZE 2048
33 #define ETH_ALEN 6
35 int dhcpmgmt=0;
36 static char *pidfile = NULL;
37 static char pidfile_path[_POSIX_PATH_MAX];
38 int logok=0;
39 char *prog;
40 extern FILE *lfd;
42 void printlog(int priority, const char *format, ...)
44 va_list arg;
46 va_start (arg, format);
48 if (logok)
49 vsyslog(priority,format,arg);
50 else {
51 fprintf(stderr,"%s: ",prog);
52 vfprintf(stderr,format,arg);
53 fprintf(stderr,"\n");
55 va_end (arg);
58 enum request_type { REQ_NEW_CONTROL };
60 #define MAXDESCR 128
62 struct request_v3 {
63 uint32_t magic;
64 uint32_t version;
65 enum request_type type;
66 struct sockaddr_un sock;
67 char description[MAXDESCR];
70 static struct sockaddr_un inpath;
72 static int send_fd(char *name, int fddata, struct sockaddr_un *datasock, int port, char *g, int m)
74 int pid = getpid();
75 struct request_v3 req;
76 int fdctl;
77 int gid;
78 struct group *gs;
79 struct passwd *callerpwd;
81 struct sockaddr_un sock;
83 callerpwd=getpwuid(getuid());
84 if((fdctl = socket(AF_UNIX, SOCK_STREAM, 0)) < 0){
85 perror("socket");
86 exit(1);
89 if (name == NULL)
90 name=VDESTDSOCK;
91 else {
92 char *split;
93 if(name[strlen(name)-1] == ']' && (split=rindex(name,'[')) != NULL) {
94 *split=0;
95 split++;
96 port=atoi(split);
97 if (*name==0) name=VDESTDSOCK;
101 sock.sun_family = AF_UNIX;
102 snprintf(sock.sun_path, sizeof(sock.sun_path), "%s/ctl", name);
103 if(connect(fdctl, (struct sockaddr *) &sock, sizeof(sock))){
104 if (name == VDESTDSOCK) {
105 name=VDETMPSOCK;
106 snprintf(sock.sun_path, sizeof(sock.sun_path), "%s/ctl", name);
107 if(connect(fdctl, (struct sockaddr *) &sock, sizeof(sock))){
108 snprintf(sock.sun_path, sizeof(sock.sun_path), "%s", name);
109 if(connect(fdctl, (struct sockaddr *) &sock, sizeof(sock))){
110 perror("connect");
111 exit(1);
117 req.magic=SWITCH_MAGIC;
118 req.version=3;
119 req.type=REQ_NEW_CONTROL+(port << 8);
120 req.sock.sun_family=AF_UNIX;
122 /* First choice, return socket from the switch close to the control dir*/
123 memset(req.sock.sun_path, 0, sizeof(req.sock.sun_path));
124 sprintf(req.sock.sun_path, "%s.%05d-%02d", name, pid, 0);
125 if(bind(fddata, (struct sockaddr *) &req.sock, sizeof(req.sock)) < 0){
126 /* if it is not possible -> /tmp */
127 memset(req.sock.sun_path, 0, sizeof(req.sock.sun_path));
128 sprintf(req.sock.sun_path, "/tmp/vde.%05d-%02d", pid, 0);
129 if(bind(fddata, (struct sockaddr *) &req.sock, sizeof(req.sock)) < 0) {
130 perror("bind");
131 exit(1);
135 snprintf(req.description,MAXDESCR,"slirpvde user=%s PID=%d SOCK=%s",
136 callerpwd->pw_name,pid,req.sock.sun_path);
137 memcpy(&inpath,&req.sock,sizeof(req.sock));
139 if (send(fdctl,&req,sizeof(req)-MAXDESCR+strlen(req.description),0) < 0) {
140 perror("send");
141 exit(1);
144 if (recv(fdctl,datasock,sizeof(struct sockaddr_un),0)<0) {
145 perror("recv");
146 exit(1);
149 if (g) {
150 if ((gs=getgrnam(g)) == NULL)
151 gid=atoi(g);
152 else
153 gid=gs->gr_gid;
154 chown(inpath.sun_path,-1,gid);
156 if (m>=0)
157 chmod(inpath.sun_path,m);
159 return fdctl;
162 static void save_pidfile()
164 if(pidfile[0] != '/')
165 strncat(pidfile_path, pidfile, PATH_MAX - strlen(pidfile_path));
166 else
167 strcpy(pidfile_path, pidfile);
169 int fd = open(pidfile_path,
170 O_WRONLY | O_CREAT | O_EXCL,
171 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
172 FILE *f;
174 if(fd == -1) {
175 printlog(LOG_ERR, "Error in pidfile creation: %s", strerror(errno));
176 exit(1);
179 if((f = fdopen(fd, "w")) == NULL) {
180 printlog(LOG_ERR, "Error in FILE* construction: %s", strerror(errno));
181 exit(1);
184 if(fprintf(f, "%ld\n", (long int)getpid()) <= 0) {
185 printlog(LOG_ERR, "Error in writing pidfile");
186 exit(1);
189 fclose(f);
192 static void cleanup(void)
194 if((pidfile != NULL) && unlink(pidfile_path) < 0) {
195 printlog(LOG_WARNING,"Couldn't remove pidfile '%s': %s", pidfile, strerror(errno));
197 unlink(inpath.sun_path);
200 static void sig_handler(int sig)
202 cleanup();
203 signal(sig, SIG_DFL);
204 kill(getpid(), sig);
207 static void setsighandlers()
209 /* setting signal handlers.
210 * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
211 * ignores all the others signals which could cause termination. */
212 struct { int sig; const char *name; int ignore; } signals[] = {
213 { SIGHUP, "SIGHUP", 0 },
214 { SIGINT, "SIGINT", 0 },
215 { SIGPIPE, "SIGPIPE", 1 },
216 { SIGALRM, "SIGALRM", 1 },
217 { SIGTERM, "SIGTERM", 0 },
218 { SIGUSR1, "SIGUSR1", 1 },
219 { SIGUSR2, "SIGUSR2", 1 },
220 { SIGPROF, "SIGPROF", 1 },
221 { SIGVTALRM, "SIGVTALRM", 1 },
222 #ifdef VDE_LINUX
223 { SIGPOLL, "SIGPOLL", 1 },
224 { SIGSTKFLT, "SIGSTKFLT", 1 },
225 { SIGIO, "SIGIO", 1 },
226 { SIGPWR, "SIGPWR", 1 },
227 { SIGUNUSED, "SIGUNUSED", 1 },
228 #endif
229 #ifdef VDE_DARWIN
230 { SIGXCPU, "SIGXCPU", 1 },
231 { SIGXFSZ, "SIGXFSZ", 1 },
232 #endif
233 { 0, NULL, 0 }
236 int i;
237 for(i = 0; signals[i].sig != 0; i++)
238 if(signal(signals[i].sig,
239 signals[i].ignore ? SIG_IGN : sig_handler) < 0)
240 perror("Setting handler");
243 unsigned char bufin[BUFSIZE];
245 int slirp_can_output(void)
247 return 1;
250 static int fddata;
251 static struct sockaddr_un dataout;
253 #if 0
254 #define convery2ascii(x) ((x)>=' ' && (x) <= '~')?(x):'.'
255 void dumppkt(const uint8_t *pkt, int pkt_len)
257 register int i,j;
258 printf("Packet dump len=%d\n",pkt_len);
259 if (pkt_len == 0)
260 return;
261 for (i=0;i<((pkt_len-1)/16)+1;i++) {
262 for (j=0;j<16;j++)
263 if (i*16+j > pkt_len)
264 printf(" ");
265 else
266 printf("%02x ",pkt[i*16+j]);
267 printf(" ");
268 for (j=0;j<16;j++)
269 if (i*16+j > pkt_len)
270 printf(" ");
271 else
272 printf("%c",convery2ascii(pkt[i*16+j]));
273 printf("\n");
276 #endif
278 void slirp_output(const uint8_t *pkt, int pkt_len)
280 /* slirp -> vde */
281 //fprintf(stderr,"RX from slirp %d\n",pkt_len);
282 //dumppkt(pkt,pkt_len);
283 sendto(fddata,pkt,pkt_len,0,(struct sockaddr *) &dataout, sizeof(struct sockaddr_un));
286 struct redirx {
287 u_int32_t inaddr;
288 int start_port;
289 int display;
290 int screen;
291 struct redirx *next;
294 struct redirtcp {
295 u_int32_t inaddr;
296 int port;
297 int lport;
298 struct redirtcp *next;
301 static struct redirtcp *parse_redir_tcp(struct redirtcp *head, char *buff)
303 u_int32_t inaddr=0;
304 int port=0;
305 int lport=0;
306 char *ipaddrstr=NULL;
307 char *portstr=NULL;
308 struct redirtcp *new;
310 if ((ipaddrstr = strchr(buff, ':'))) {
311 *ipaddrstr++ = 0;
312 if (*ipaddrstr == 0) {
313 fprintf(stderr,"redir TCP syntax error\n");
314 return head;
317 if ((portstr = strchr(ipaddrstr, ':'))) {
318 *portstr++ = 0;
319 if (*portstr == 0) {
320 fprintf(stderr,"redir TCP syntax error\n");
321 return head;
325 sscanf(buff,"%d",&lport);
326 sscanf(portstr,"%d",&port);
327 if (ipaddrstr)
328 inaddr = inet_addr(ipaddrstr);
330 if (!inaddr) {
331 lprint(stderr,"TCP redirection error: an IP address must be specified\r\n");
332 return head;
335 if ((new=malloc(sizeof(struct redirtcp)))==NULL)
336 return head;
337 else {
338 new->inaddr=inaddr;
339 new->port=port;
340 new->lport=lport;
341 new->next=head;
342 return new;
346 static struct redirx *parse_redir_x(struct redirx *head, char *buff)
348 char *ptr=NULL;
349 u_int32_t inaddr = 0;
350 int display=0;
351 int screen=0;
352 int start_port = 0;
353 struct redirx *new;
354 if ((ptr = strchr(buff, ':'))) {
355 *ptr++ = 0;
356 if (*ptr == 0) {
357 fprintf(stderr,"X-redirection syntax error\n");
358 return head;
361 if (buff[0]) {
362 inaddr = inet_addr(buff);
363 if (inaddr == 0xffffffff) {
364 lprint(stderr,"Error: X-redirection bad address\r\n");
365 return head;
368 if (ptr) {
369 if (strchr(ptr, '.')) {
370 if (sscanf(ptr, "%d.%d", &display, &screen) != 2)
371 return head;
372 } else {
373 if (sscanf(ptr, "%d", &display) != 1)
374 return head;
378 if (!inaddr) {
379 lprint(stderr,"Error: X-redirection an IP address must be specified\r\n");
380 return head;
383 if ((new=malloc(sizeof(struct redirx)))==NULL)
384 return head;
385 else {
386 new->inaddr=inaddr;
387 new->display=display;
388 new->screen=screen;
389 new->start_port=start_port;
390 new->next=head;
391 return new;
395 static void do_redir_tcp(struct redirtcp *head)
397 if (head) {
398 do_redir_tcp(head->next);
399 redir_tcp(head->inaddr,head->port,head->lport);
400 free(head);
404 static void do_redir_x(struct redirx *head)
406 if (head) {
407 do_redir_x(head->next);
408 redir_x(head->inaddr,head->start_port,head->display,head->screen);
409 free(head);
413 void usage(char *name) {
414 fprintf(stderr,"Usage: %s [-socket vdesock] [-dhcp] [-daemon] [-network netaddr] \n\t%s [-s vdesock] [-D] [-d] [-n netaddr]\n",name,name);
415 exit(-1);
418 struct option slirpvdeopts[] = {
419 {"socket",1,NULL,'s'},
420 {"sock",1,NULL,'s'},
421 {"vdesock",1,NULL,'s'},
422 {"unix",1,NULL,'s'},
423 {"pidfile", 1, 0, 'p'},
424 {"dhcp",0,NULL,'D'},
425 {"daemon",0,NULL,'d'},
426 {"network",0,NULL,'n'},
427 {"mod",1,0,'m'},
428 {"group",1,0,'g'},
429 {"port",1,0,'P'},
430 {NULL,0,0,0}};
432 int main(int argc, char **argv)
434 char *sockname=NULL;
435 int result,nfds;
436 int port=0;
437 int connected_fd;
438 register ssize_t nx;
439 register int i;
440 fd_set rs,ws,xs;
441 int opt,longindx;
442 char *netw=NULL;
443 char *group=NULL;
444 int mode=0700;
445 int daemonize=0;
446 struct redirtcp *rtcp=NULL;
447 struct redirx *rx=NULL;
449 prog=basename(argv[0]);
451 while ((opt=GETOPT_LONG(argc,argv,"s:n:p:g:m:L:X:dD",slirpvdeopts,&longindx)) > 0) {
452 switch (opt) {
453 case 's' : sockname=optarg;
454 break;
455 case 'D' : dhcpmgmt = 1;
456 break;
457 case 'd' : daemonize = 1;
458 break;
459 case 'n' : netw=optarg;
460 break;
461 case 'm' : sscanf(optarg,"%o",&mode);
462 break;
463 case 'g' : group=strdup(optarg);
464 break;
465 case 'p': pidfile=strdup(optarg);
466 break;
467 case 'P' : port=atoi(optarg);
468 break;
469 case 'L': rtcp=parse_redir_tcp(rtcp,optarg);
470 break;
471 case 'X': rx=parse_redir_x(rx,optarg);
472 break;
473 default : usage(prog);
474 break;
477 atexit(cleanup);
478 if (daemonize) {
479 openlog(basename(prog), LOG_PID, 0);
480 logok=1;
481 syslog(LOG_INFO,"slirpvde started");
483 if(getcwd(pidfile_path, PATH_MAX-1) == NULL) {
484 printlog(LOG_ERR, "getcwd: %s", strerror(errno));
485 exit(1);
487 strcat(pidfile_path, "/");
488 if (daemonize && daemon(0, 1)) {
489 printlog(LOG_ERR,"daemon: %s",strerror(errno));
490 exit(1);
493 if((fddata = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0){
494 perror("socket");
495 exit(1);
497 connected_fd=send_fd(sockname, fddata, &dataout, port, group, mode);
498 lfd=stderr;
499 slirp_init(netw);
501 do_redir_tcp(rtcp);
502 do_redir_x(rx);
504 for(;;) {
505 FD_ZERO(&rs);
506 FD_ZERO(&ws);
507 FD_ZERO(&xs);
508 nfds= -1;
509 slirp_select_fill(&nfds,&rs,&ws,&xs);
510 FD_SET(fddata,&rs);
511 FD_SET(connected_fd,&rs);
512 if (fddata>nfds) nfds=fddata;
513 if (connected_fd>nfds) nfds=connected_fd;
514 result=select(nfds+1,&rs,&ws,&xs,NULL);
515 //printf("SELECT %d %d\n",nfds,result);
516 if (FD_ISSET(fddata,&rs)) {
517 nx=recv(fddata,bufin,BUFSIZE,0);
518 //fprintf(stderr,"TX to slirp %d\n",nx);
519 result--;
520 slirp_input(bufin,nx);
521 //fprintf(stderr,"TX to slirp %d exit\n",nx);
523 if (result > 0) {
524 //fprintf(stderr,"slirp poll\n");
525 slirp_select_poll(&rs,&ws,&xs);
526 //fprintf(stderr,"slirp poll exit\n");
528 if (FD_ISSET(connected_fd,&rs)) {
529 if(read(connected_fd,bufin,BUFSIZE)==0)
530 exit(0);
533 return(0);