syslimits.h fix for OSX
[vde.git] / vde / vde_switch.c
blob03e7d20fd4d4b49217e0c831ebb8bcb542aab581
1 /* Copyright 2001, 2002 Jeff Dike and others
2 * Copyright 2003 Renzo Davoli (modified for daemon and vde)
3 * Licensed under the GPL
4 * Modified for --pidfile/-p and better cleanup management by Mattia Belletti.
5 */
7 #include <config.h>
8 #include <stdio.h>
9 #include <errno.h>
10 #include <stdlib.h>
11 #include <signal.h>
12 #include <fcntl.h>
13 #include <stdint.h>
14 #include <getopt.h>
15 #include <sys/socket.h>
16 #include <sys/un.h>
17 #include <sys/poll.h>
18 #include <sys/stat.h>
19 #include <sys/time.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22 #include <syslog.h>
23 #include <libgen.h>
24 #include <endian.h>
25 #include <vde.h>
26 #include <switch.h>
27 #include <port.h>
28 #include <hash.h>
29 #ifdef TUNTAP
30 #include <tuntap.h>
31 #endif
33 #ifdef notdef
34 #include <stddef.h>
35 #endif
36 #include <stdarg.h>
38 static int hub = 0;
39 static char *prog;
40 static int daemonize = 0;
41 static int logok = 0;
43 void printlog(int priority, const char *format, ...)
45 va_list arg;
47 va_start (arg, format);
49 if (logok)
50 vsyslog(priority,format,arg);
51 else {
52 fprintf(stderr,"%s: ",prog);
53 vfprintf(stderr,format,arg);
54 fprintf(stderr,"\n");
56 va_end (arg);
60 enum request_type { REQ_NEW_CONTROL };
62 #define SWITCH_MAGIC 0xfeedface
64 struct request_v1 {
65 uint32_t magic;
66 enum request_type type;
67 union {
68 struct {
69 unsigned char addr[ETH_ALEN];
70 struct sockaddr_un name;
71 } new_control;
72 } u;
75 struct request_v3 {
76 uint32_t magic;
77 uint32_t version;
78 enum request_type type;
79 struct sockaddr_un sock;
82 union request {
83 struct request_v1 v1;
84 struct request_v3 v3;
87 static char *ctl_socket = VDESTDSOCK;
88 static int ctl_socket_created = 0;
90 static char *data_socket = NULL;
91 static struct sockaddr_un data_sun;
93 static char *pidfile = NULL;
94 static char pidfile_path[_POSIX_PATH_MAX];
96 static void cleanup(void)
98 if(ctl_socket_created && unlink(ctl_socket) < 0){
99 printlog(LOG_WARNING,"Couldn't remove control socket '%s' : %s", ctl_socket, strerror(errno));
101 if((data_socket != NULL) && (unlink(data_socket) < 0)){
102 printlog(LOG_WARNING,"Couldn't remove data socket '%s' : %s", data_socket, strerror(errno));
104 if((pidfile != NULL) && unlink(pidfile_path) < 0) {
105 printlog(LOG_WARNING,"Couldn't remove pidfile '%s': %s", pidfile, strerror(errno));
109 /* array of pointer to struct port */
110 void **g_fdsdata = NULL;
111 int g_nfds = 0;
112 int g_minfds = 0;
113 static struct pollfd *fds = NULL;
114 static int maxfds = 0;
115 static int nfds = 0;
117 /* adds file descriptor 'fd' to the set of file descriptors whose input we wait
118 * for in main loop */
119 static void add_fd(int fd)
121 struct pollfd *p;
123 /* enlarge fds and g_fdsdata array if needed */
124 if(nfds == maxfds){
125 maxfds = maxfds ? 2 * maxfds : 8;
126 if((fds = realloc(fds, maxfds * sizeof(struct pollfd))) == NULL){
127 printlog(LOG_ERR,"realloc fds %s",strerror(errno));
128 exit(1);
130 if((g_fdsdata = realloc(g_fdsdata, maxfds * sizeof(void *))) == NULL){
131 printlog(LOG_ERR,"realloc fdsdata %s",strerror(errno));
132 exit(1);
135 p = &fds[nfds];
136 p->fd = fd;
137 p->events = POLLIN;
138 g_fdsdata[nfds]=NULL;
139 nfds++;
140 g_nfds=nfds;
143 static void remove_fd(int fd)
145 int i;
147 for(i = 0; i < nfds; i++){
148 if(fds[i].fd == fd) break;
150 if(i == nfds){
151 printlog(LOG_WARNING,"remove_fd : Couldn't find descriptor %d", fd);
152 } else {
153 memmove(&fds[i], &fds[i + 1], (maxfds - i - 1) * sizeof(struct pollfd));
154 memmove(&g_fdsdata[i], &g_fdsdata[i + 1], (maxfds - i - 1) * sizeof(void *));
155 nfds--;
156 g_nfds=nfds;
160 static void sig_handler(int sig)
162 printlog(LOG_ERR,"Caught signal %d, cleaning up and exiting", sig);
163 cleanup();
164 signal(sig, SIG_DFL);
165 kill(getpid(), sig);
168 static void close_descriptor(int i, int fd)
170 close_port(i,fd);
171 close(fd);
172 remove_fd(fd);
175 static void new_port_v1_v3(int i, int fd, enum request_type type_group,
176 struct sockaddr_un *sock, int data_fd)
178 int n, err;
179 enum request_type type = type_group & 0xff;
180 int group=type_group >> 8;
182 // group
183 switch(type){
184 case REQ_NEW_CONTROL:
185 err = setup_sock_port(i, fd, sock, data_fd, group);
186 if(err) return;
187 n = write(fd, &data_sun, sizeof(data_sun));
188 if(n != sizeof(data_sun)){
189 printlog(LOG_WARNING,"Sending data socket name %s",strerror(errno));
190 close_descriptor(i, fd);
192 break;
193 default:
194 printlog(LOG_WARNING,"Bad request type : %d", type);
195 close_descriptor(i, fd);
199 static void new_port(int i, int fd, int data_fd)
201 union request req;
202 int len;
204 len = read(fd, &req, sizeof(req));
205 if(len < 0){
206 if(errno != EAGAIN){
207 printlog(LOG_WARNING,"Reading request %s", strerror(errno));
208 close_descriptor(i, fd);
210 return;
212 else if(len == 0){
213 printlog(LOG_WARNING,"EOF from new port");
214 close_descriptor(i, fd);
215 return;
217 if(req.v1.magic == SWITCH_MAGIC){
218 if(req.v3.version == 3)
219 new_port_v1_v3(i,fd, req.v3.type, &req.v3.sock, data_fd);
220 else if(req.v3.version > 2 || req.v3.version == 2)
221 printlog(LOG_ERR, "Request for a version %d port, which this "
222 "vde_switch doesn't support", req.v3.version);
223 else new_port_v1_v3(i, fd, req.v1.type, &req.v1.u.new_control.name, data_fd);
225 else {
226 printlog(LOG_WARNING,"V0 request not supported");
227 close_descriptor(i, fd);
228 return;
232 /* accept a new connection from socket fd, then set received connection as non
233 * blocking and add it between the fds whose input we wait for */
234 void accept_connection(int fd)
236 struct sockaddr addr;
237 int len, new;
239 len = sizeof(addr);
240 new = accept(fd, &addr, &len);
241 if(new < 0){
242 printlog(LOG_WARNING,"accept %s",strerror(errno));
243 return;
245 if(fcntl(new, F_SETFL, O_NONBLOCK) < 0){
246 printlog(LOG_WARNING,"fcntl - setting O_NONBLOCK %s",strerror(errno));
247 close(new);
248 return;
250 add_fd(new);
253 /* check to see if given unix socket is still in use; if it isn't, remove the
254 * socket from the file system */
255 int still_used(struct sockaddr_un *sun)
257 int test_fd, ret = 1;
259 if((test_fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
260 printlog(LOG_ERR,"socket %s",strerror(errno));
261 exit(1);
263 if(connect(test_fd, (struct sockaddr *) sun, sizeof(*sun)) < 0){
264 if(errno == ECONNREFUSED){
265 if(unlink(sun->sun_path) < 0){
266 printlog(LOG_ERR,"Failed to removed unused socket '%s': %s",
267 sun->sun_path,strerror(errno));
269 ret = 0;
271 else printlog(LOG_ERR,"connect %s",strerror(errno));
273 close(test_fd);
274 return(ret);
277 /* try to bind socket fd to unix socket at path name; resulting sockaddr is
278 * returned in sock_out if != NULL; returns 0 if alright, or the error
279 * otherwise. */
280 int bind_socket(int fd, const char *name, struct sockaddr_un *sock_out)
282 struct sockaddr_un sun;
284 sun.sun_family = AF_UNIX;
285 strncpy(sun.sun_path, name, sizeof(sun.sun_path));
287 if(bind(fd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
288 if((errno == EADDRINUSE) && still_used(&sun)) return(EADDRINUSE);
289 else if(bind(fd, (struct sockaddr *) &sun, sizeof(sun)) < 0){
290 printlog(LOG_ERR,"bind %s",strerror(errno));
291 return(EPERM);
294 if(sock_out != NULL) *sock_out = sun;
295 return(0);
299 /* bind the data socket as an unix socket in the abstract namespace (as
300 * detailed in man 7 unix), exit with error from the program if it fails.
301 * obtained sockaddr is returned in sun, which must not be NULL. */
302 void bind_data_socket(int fd, struct sockaddr_un *sun)
304 struct {
305 char zero;
306 int pid;
307 int usecs;
308 } name;
309 struct timeval tv;
311 name.zero = 0;
312 name.pid = getpid();
313 gettimeofday(&tv, NULL);
314 name.usecs = tv.tv_usec;
315 sun->sun_family = AF_UNIX;
316 memcpy(sun->sun_path, &name, sizeof(name));
317 if(bind(fd, (struct sockaddr *) sun, sizeof(*sun)) < 0){
318 printlog(LOG_ERR,"Binding to data socket %s",strerror(errno));
319 exit(1);
323 /* try to bind both control socket ctl_fd to file ctl_name and data socket
324 * data_fd, as detailed in bind_socket and bind_data_socket, and exit from the
325 * program with error if it can't */
326 void bind_sockets(int ctl_fd, const char *ctl_name, int data_fd)
328 int err, used=0;
330 /* do it in advance, so we avoid race conditions */
331 ctl_socket_created = 1;
333 err = bind_socket(ctl_fd, ctl_name, NULL);
334 if(err == 0){
335 bind_data_socket(data_fd, &data_sun);
336 return;
338 else if(err == EADDRINUSE) used = 1;
340 if(used){
341 fprintf(stderr, "The control socket '%s' has another server "
342 "attached to it\n", ctl_name);
343 fprintf(stderr, "You can either\n");
344 fprintf(stderr, "\tremove '%s'\n", ctl_name);
345 fprintf(stderr, "\tor rerun with a different, unused filename for a "
346 "socket\n");
348 else
349 fprintf(stderr, "The control socket '%s' exists, isn't used, but couldn't "
350 "be removed\n", ctl_name);
351 exit(1);
354 static void save_pidfile()
356 if(pidfile[0] != '/')
357 strncat(pidfile_path, pidfile, PATH_MAX - strlen(pidfile_path));
358 else
359 strcpy(pidfile_path, pidfile);
361 int fd = open(pidfile_path,
362 O_WRONLY | O_CREAT | O_EXCL,
363 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
364 FILE *f;
366 if(fd == -1) {
367 printlog(LOG_ERR, "Error in pidfile creation: %s", strerror(errno));
368 exit(1);
371 if((f = fdopen(fd, "w")) == NULL) {
372 printlog(LOG_ERR, "Error in FILE* construction: %s", strerror(errno));
373 exit(1);
376 if(fprintf(f, "%ld\n", (long int)getpid()) <= 0) {
377 printlog(LOG_ERR, "Error in writing pidfile");
378 exit(1);
381 fclose(f);
384 static void Usage(void)
386 printf(
387 "Usage: vde_switch [OPTIONS]\n"
388 "Runs a VDE switch.\n"
389 "\n"
390 " -s, --sock SOCK Choose name of the control UNIX socket\n"
391 " -s, --vdesock SOCK Same as --sock SOCK\n"
392 " -s, --unix SOCK Same as --sock SOCK\n"
393 #ifdef TUNTAP
394 " -t, --tap TAP Enable routing through TAP tap interface\n"
395 #endif
396 " -d, --daemon Daemonize vde_switch once run\n"
397 " -x, --hub Make the switch act as a hub\n"
398 " -p, --pidfile PIDFILE Write pid of daemon to PIDFILE\n"
399 " -h, --help Display this help and exit\n"
400 " -v, --version Display informations on version and exit\n"
401 "\n"
402 "Report bugs to PACKAGE_BUGREPORT\n"
404 exit(1);
407 static void version(void)
409 printf(
410 "VDE " PACKAGE_VERSION "\n"
411 "Copyright (C) 2001, 2002 Jeff Dike and others\n"
412 "Copyright 2003 Renzo Davoli (modified for daemon and vde)\n"
413 "VDE comes with NO WARRANTY, to the extent permitted by law.\n"
414 "You may redistribute copies of VDE under the terms of the\n"
415 "GNU General Public License.\n"
416 "For more information about these matters, see the files\n"
417 "named COPYING.\n");
418 exit(0);
421 int main(int argc, char **argv)
423 int connect_fd, data_fd, n, i, /*new,*/ one = 1;
424 #ifdef TUNTAP
425 char *tap_dev = NULL;
426 int tap_fd = -1;
427 #endif
429 atexit(cleanup);
430 prog = argv[0];
431 /* option parsing */
433 int c;
434 while (1) {
435 int option_index = 0;
437 static struct option long_options[] = {
438 {"sock", 1, 0, 's'},
439 {"vdesock", 1, 0, 's'},
440 {"unix", 1, 0, 's'},
441 {"tap", 1, 0, 't'},
442 {"daemon", 0, 0, 'd'},
443 {"hub", 0, 0, 'x'},
444 {"pidfile", 1, 0, 'p'},
445 {"help",0 , 0, 'h'},
446 {"version", 0, 0, 'v'},
447 {0, 0, 0, 0}
449 c = getopt_long_only (argc, argv, "s:t:dxp:h",
450 long_options, &option_index);
451 if (c == -1)
452 break;
453 switch (c) {
454 case 's':
455 ctl_socket=strdup(optarg);
456 break;
458 case 't':
459 #ifdef TUNTAP
460 tap_dev=strdup(optarg);
461 #else
462 fprintf(stderr, "-tap isn't supported since TUNTAP isn't enabled\n");
463 Usage();
464 #endif
465 break;
466 case 'x':
467 printlog(LOG_INFO,"s will be a hub instead of a switch", prog);
468 hub = 1;
469 break;
470 case 'd':
471 daemonize=1;
472 break;
473 case 'p':
474 pidfile=strdup(optarg);
475 break;
476 case 'v':
477 version();
478 case 'h':
479 default:
480 Usage();
483 if(optind < argc)
484 Usage();
488 /* connect_fd is a stream UNIX socket, whose address can be reused, and set
489 * nonblocking */
490 if((connect_fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0){
491 printlog(LOG_ERR,"socket: %s",strerror(errno));
492 exit(1);
494 if(setsockopt(connect_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one,
495 sizeof(one)) < 0){
496 printlog(LOG_ERR,"setsockopt: %s",strerror(errno));
497 exit(1);
499 if(fcntl(connect_fd, F_SETFL, O_NONBLOCK) < 0){
500 printlog(LOG_ERR,"Setting O_NONBLOCK on connection fd: %s",strerror(errno));
501 exit(1);
503 /* data_fd is similar, but datagram */
504 if((data_fd = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0){
505 printlog(LOG_ERR,"socket: %s",strerror(errno));
506 exit(1);
508 if(fcntl(data_fd, F_SETFL, O_NONBLOCK) < 0){
509 printlog(LOG_ERR,"Setting O_NONBLOCK on data fd %s",strerror(errno));
510 exit(1);
513 /* make needed binds to complete connect_fd and data_fd initialization */
514 bind_sockets(connect_fd, ctl_socket, data_fd);
516 /* tell that connect_fd will be able to accept connections */
517 if(listen(connect_fd, 15) < 0){
518 printlog(LOG_ERR,"listen: %s",strerror(errno));
519 exit(1);
523 /* setting signal handlers.
524 * sets clean termination for SIGHUP, SIGINT and SIGTERM, and simply
525 * ignores all the others signals which could cause termination. */
526 struct { int sig; const char *name; int ignore; } signals[] = {
527 { SIGHUP, "SIGHUP", 0 },
528 { SIGINT, "SIGINT", 0 },
529 { SIGPIPE, "SIGPIPE", 1 },
530 { SIGALRM, "SIGALRM", 1 },
531 { SIGTERM, "SIGTERM", 0 },
532 { SIGUSR1, "SIGUSR1", 1 },
533 { SIGUSR2, "SIGUSR2", 1 },
534 { SIGPOLL, "SIGPOLL", 1 },
535 { SIGPROF, "SIGPROF", 1 },
536 { SIGVTALRM, "SIGVTALRM", 1 },
537 { SIGSTKFLT, "SIGSTKFLT", 1 },
538 { SIGIO, "SIGIO", 1 },
539 { SIGPWR, "SIGPWR", 1 },
540 { SIGUNUSED, "SIGUNUSED", 1 },
541 { 0, NULL, 0 }
543 int i;
545 for(i = 0; signals[i].sig != 0; i++)
546 if(signal(signals[i].sig,
547 signals[i].ignore ? SIG_IGN : sig_handler) < 0)
548 printlog(LOG_ERR,"Setting handler for %s: %s", signals[i].name,
549 strerror(errno));
552 /* initialize ARP table */
553 hash_init();
555 /* take care about logging */
556 if (daemonize) {
557 openlog(basename(prog), LOG_PID, 0);
558 logok=1;
559 syslog(LOG_INFO,"VDE_SWITCH started");
561 printlog(LOG_INFO,"attached to unix socket '%s'", ctl_socket);
563 /* add stdin (if tty), connect and data fds to the set of fds we wait for
564 * input */
565 if(isatty(0) && ! daemonize)
566 add_fd(0);
567 add_fd(connect_fd);
568 add_fd(data_fd);
569 g_minfds=g_nfds;
571 #ifdef TUNTAP
572 if(tap_dev != NULL) tap_fd = open_tap(tap_dev);
573 if(tap_fd > -1) {
574 add_fd(tap_fd);
575 setup_port(g_nfds-1, tap_fd, send_tap, NULL, 0, 0);
577 #endif
579 /* saves current path in pidfile_path, because otherwise with daemonize() we
580 * forget it */
581 if(getcwd(pidfile_path, PATH_MAX-1) == NULL) {
582 printlog(LOG_ERR, "getcwd: %s", strerror(errno));
583 exit(1);
585 strcat(pidfile_path, "/");
586 if (daemonize && daemon(0, 1)) {
587 printlog(LOG_ERR,"daemon: %s",strerror(errno));
588 exit(1);
591 /* once here, we're sure we're the true process which will continue as a
592 * server: save PID file if needed */
593 if(pidfile) save_pidfile();
595 /* main loop of events processing */
596 while(1){
597 char buf[128];
599 /* wait for some input */
600 n = poll(fds, nfds, -1);
601 if(n < 0){
602 if(errno == EINTR) continue;
603 printlog(LOG_WARNING,"poll %s",strerror(errno));
604 break;
607 /* check the input received */
608 for(i = 0; i < nfds; i++){
609 if(fds[i].revents == 0) continue;
611 /* special case for standard input */
612 if(fds[i].fd == 0){
613 /* POLLHUP; e.g., pressed Ctrl+C on a non-daemonized vde_switch; thus
614 * exit! */
615 if(fds[i].revents & POLLHUP){
616 printlog(LOG_WARNING,"EOF on stdin, cleaning up and exiting");
617 exit(0);
620 /* otherwise read data from stdin, log it, and eventually catch EOFs as
621 * exits */
622 n = read(0, buf, sizeof(buf));
623 if(n < 0){
624 printlog(LOG_WARNING,"Reading from stdin %s",strerror(errno));
625 break;
627 else if(n == 0){
628 printlog(LOG_WARNING,"EOF on stdin, cleaning up and exiting");
629 exit(0);
633 /* special case for connect fd - is there some connection pending? */
634 else if(fds[i].fd == connect_fd){
635 if(fds[i].revents & POLLHUP){
636 printlog(LOG_WARNING,"Error on connection fd");
637 continue;
639 accept_connection(connect_fd);
642 /* data socket - ??? */
643 else if(fds[i].fd == data_fd) handle_sock_data(data_fd, hub);
645 #ifdef TUNTAP
646 /* special case for tap data */
647 else if(fds[i].fd == tap_fd) handle_tap_data(i, tap_fd, hub);
648 #endif
649 else {
650 if (g_fdsdata[i] == NULL)
651 new_port(i,fds[i].fd, data_fd);
652 else
653 if (handle_sock_direct_data(i, fds[i].fd, hub))
654 close_descriptor(i, fds[i].fd);
658 return 0;