X-Git-Url: https://git.librecmc.org/?p=oweals%2Ftinc.git;a=blobdiff_plain;f=src%2Fnet.c;h=2e06a39e30b11afd75e9073087da30fac980c20e;hp=bd991e905689ea910fdfa0101467718eeb3c062a;hb=7f87c3d9134612041d56180ea7fc3e6c37991f6b;hpb=d38772ebc42f5ad1d946ee89d955f5d43bb2fe8c diff --git a/src/net.c b/src/net.c index bd991e9..2e06a39 100644 --- a/src/net.c +++ b/src/net.c @@ -17,31 +17,47 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id: net.c,v 1.35.4.62 2000/11/04 14:16:46 zarq Exp $ + $Id: net.c,v 1.35.4.74 2000/11/15 22:07:36 zarq Exp $ */ #include "config.h" -#include #include #include -#include -#include #include #include #include #include #include #include -#include #include #include #include #include #include -#include -#include -#include +/* SunOS really wants sys/socket.h BEFORE net/if.h, + and FreeBSD wants these lines below the rest. */ +#include +#include +#include + +#ifdef HAVE_OPENSSL_RAND_H +# include +#else +# include +#endif + +#ifdef HAVE_OPENSSL_EVP_H +# include +#else +# include +#endif + +#ifdef HAVE_OPENSSL_ERR_H +# include +#else +# include +#endif #ifdef HAVE_TUNTAP #include LINUX_IF_TUN_H @@ -52,6 +68,7 @@ #include "conf.h" #include "connlist.h" +#include "list.h" #include "meta.h" #include "net.h" #include "netutl.h" @@ -74,21 +91,89 @@ int keylifetime = 0; int keyexpires = 0; char *unknown = NULL; -char *interface_name = NULL; /* Contains the name of the interface */ subnet_t mymac; +list_t *child_pids; + +void _execute_script(const char *name) +{ + int error = 0; + char *scriptname; + char *s; + int fd; + + if(netname) + { + asprintf(&s, "NETNAME=%s", netname); + putenv(s); /* Don't free s! see man 3 putenv */ + } +#ifdef HAVE_UNSETENV + else + { + unsetenv("NETNAME"); + } +#endif + + if(chdir(confbase) < 0) + /* This cannot fail since we already read config files from this + directory. - Guus */ + /* Yes this can fail, somebody could have removed this directory + when we didn't pay attention. - Ivo */ + { + if(chdir("/") < 0) + /* Now if THIS fails, something wicked is going on. - Ivo */ + syslog(LOG_ERR, _("Couldn't chdir to `/': %m")); + + /* Continue anyway. */ + } + + asprintf(&scriptname, "%s/%s", confbase, name); + + /* Close all file descriptors */ + closelog(); + fcloseall(); + + /* Open standard input */ + if(open("/dev/null", O_RDONLY) < 0) + { + syslog(LOG_ERR, _("Opening `/dev/null' failed: %m")); + error = 1; + } + + if(!error) + { + /* Standard output directly goes to syslog */ + openlog(name, LOG_CONS | LOG_PID, LOG_DAEMON); + /* Standard error as well */ + if(dup2(1, 2) < 0) + { + syslog(LOG_ERR, _("System call `%s' failed: %m"), + "dup2"); + error = 1; + } + } + + if(error && debug_lvl > 1) + syslog(LOG_INFO, _("This means that any output the script generates will not be shown in syslog.")); + + execl(scriptname, NULL); + /* No return on success */ + + if(errno != ENOENT) /* Ignore if the file does not exist */ + syslog(LOG_WARNING, _("Error executing `%s': %m"), scriptname); + + /* No need to free things */ + exit(0); +} + /* Execute the given script. This function doesn't really belong here. */ -int execute_script(const char* name) +int execute_script(const char *name) { - char *scriptname; pid_t pid; - char *s; - - asprintf(&scriptname, "%s/%s", confbase, name); if((pid = fork()) < 0) { @@ -99,22 +184,42 @@ int execute_script(const char* name) if(pid) { - free(scriptname); + list_append(child_pids, pid); return 0; } /* Child here */ - asprintf(&s, "IFNAME=%s", interface_name); - putenv(s); - execl(scriptname, NULL); - /* No return on success */ - - if(errno != ENOENT) /* Ignore if the file does not exist */ - syslog(LOG_WARNING, _("Error executing `%s': %m"), scriptname); + _execute_script(name); +} - /* No need to free things */ - exit(0); +int check_child(void *data) +{ + pid_t pid; + int status; + + pid = (pid_t) data; + pid = waitpid(pid, &status, WNOHANG); + if(WIFEXITED(status)) + { + if(WIFSIGNALED(status)) /* Child was killed by a signal */ + { + syslog(LOG_ERR, _("Child with PID %d was killed by signal %d (%s)"), + pid, WTERMSIG(status), strsignal(WTERMSIG(status))); + return -1; + } + if(WEXITSTATUS(status) != 0) + { + syslog(LOG_INFO, _("Child with PID %d exited with code %d"), + WEXITSTATUS(status)); + } + return -1; + } +} + +void check_children(void) +{ + list_forall_nodes(child_pids, check_child); } int xsend(conn_list_t *cl, vpn_packet_t *inpkt) @@ -143,8 +248,6 @@ cp total_socket_out += outlen; - cl->want_ping = 1; - if((send(cl->socket, (char *) &(outpkt.len), outlen, 0)) < 0) { syslog(LOG_ERR, _("Error sending packet to %s (%s): %m"), @@ -155,7 +258,7 @@ cp return 0; } -int xrecv(vpn_packet_t *inpkt) +int xrecv(conn_list_t *cl, vpn_packet_t *inpkt) { vpn_packet_t outpkt; int outlen, outpad; @@ -279,7 +382,7 @@ cp returned a zero exit code */ void flush_queue(conn_list_t *cl, packet_queue_t **pq, - int (*function)(conn_list_t*,void*)) + int (*function)(conn_list_t*,vpn_packet_t*)) { queue_element_t *p, *next = NULL; cp @@ -341,14 +444,27 @@ cp } return -1; - } + } cl = subnet->owner; + if(cl == myself) + { + if(debug_lvl >= DEBUG_TRAFFIC) + { + syslog(LOG_NOTICE, _("Packet with destination %d.%d.%d.%d is looping back to us!"), + IP_ADDR_V(to)); + } + + return -1; + } + /* If we ourselves have indirectdata flag set, we should send only to our uplink! */ /* FIXME - check for indirection and reprogram it The Right Way(tm) this time. */ + /* Connections are now opened beforehand... + if(!cl->status.dataopen) if(setup_vpn_connection(cl) < 0) { @@ -356,6 +472,7 @@ cp cl->name, cl->hostname); return -1; } + */ if(!cl->status.validkey) { @@ -397,7 +514,7 @@ int setup_tap_fd(void) struct ifreq ifr; cp - if((cfg = get_config_val(config, tapdevice))) + if((cfg = get_config_val(config, config_tapdevice))) tapfname = cfg->data.ptr; else #ifdef HAVE_TUNTAP @@ -434,18 +551,11 @@ cp strncpy(ifr.ifr_name, netname, IFNAMSIZ); cp if (!ioctl(tap_fd, TUNSETIFF, (void *) &ifr)) - { + { syslog(LOG_INFO, _("%s is a new style tun/tap device"), tapfname); taptype = TAP_TYPE_TUNTAP; } #endif - - /* Add name of network interface to environment (for scripts) */ - - ioctl(tap_fd, SIOCGIFNAME, (void *) &ifr); - interface_name = xmalloc(strlen(ifr.ifr_name)); - strcpy(interface_name, ifr.ifr_name); - cp return 0; } @@ -469,6 +579,7 @@ cp if(setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) { + close(nfd); syslog(LOG_ERR, _("System call `%s' failed: %m"), "setsockopt"); return -1; @@ -476,6 +587,7 @@ cp if(setsockopt(nfd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one))) { + close(nfd); syslog(LOG_ERR, _("System call `%s' failed: %m"), "setsockopt"); return -1; @@ -484,15 +596,17 @@ cp flags = fcntl(nfd, F_GETFL); if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0) { + close(nfd); syslog(LOG_ERR, _("System call `%s' failed: %m"), "fcntl"); return -1; } - if((cfg = get_config_val(config, interface))) + if((cfg = get_config_val(config, config_interface))) { if(setsockopt(nfd, SOL_SOCKET, SO_KEEPALIVE, cfg->data.ptr, strlen(cfg->data.ptr))) { + close(nfd); syslog(LOG_ERR, _("Unable to bind listen socket to interface %s: %m"), cfg->data.ptr); return -1; } @@ -502,19 +616,21 @@ cp a.sin_family = AF_INET; a.sin_port = htons(port); - if((cfg = get_config_val(config, interfaceip))) + if((cfg = get_config_val(config, config_interfaceip))) a.sin_addr.s_addr = htonl(cfg->data.ip->address); else a.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(nfd, (struct sockaddr *)&a, sizeof(struct sockaddr))) { + close(nfd); syslog(LOG_ERR, _("Can't bind to port %hd/tcp: %m"), port); return -1; } if(listen(nfd, 3)) { + close(nfd); syslog(LOG_ERR, _("System call `%s' failed: %m"), "listen"); return -1; @@ -535,12 +651,14 @@ int setup_vpn_in_socket(int port) cp if((nfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + close(nfd); syslog(LOG_ERR, _("Creating socket failed: %m")); return -1; } if(setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) { + close(nfd); syslog(LOG_ERR, _("System call `%s' failed: %m"), "setsockopt"); return -1; @@ -549,6 +667,7 @@ cp flags = fcntl(nfd, F_GETFL); if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0) { + close(nfd); syslog(LOG_ERR, _("System call `%s' failed: %m"), "fcntl"); return -1; @@ -561,6 +680,7 @@ cp if(bind(nfd, (struct sockaddr *)&a, sizeof(struct sockaddr))) { + close(nfd); syslog(LOG_ERR, _("Can't bind to port %hd/udp: %m"), port); return -1; } @@ -580,7 +700,7 @@ cp if(debug_lvl >= DEBUG_CONNECTIONS) syslog(LOG_INFO, _("Trying to connect to %s"), cl->hostname); - if((cfg = get_config_val(cl->config, port)) == NULL) + if((cfg = get_config_val(cl->config, config_port)) == NULL) cl->port = 655; else cl->port = cfg->data.val; @@ -599,6 +719,7 @@ cp if(connect(cl->meta_socket, (struct sockaddr *)&a, sizeof(a)) == -1) { + close(cl->meta_socket); syslog(LOG_ERR, _("%s port %hd: %m"), cl->hostname, cl->port); return -1; } @@ -606,6 +727,7 @@ cp flags = fcntl(cl->meta_socket, F_GETFL); if(fcntl(cl->meta_socket, F_SETFL, flags | O_NONBLOCK) < 0) { + close(cl->meta_socket); syslog(LOG_ERR, _("fcntl for %s port %d: %m"), cl->hostname, cl->port); return -1; @@ -649,7 +771,7 @@ cp return -1; } - if(!(cfg = get_config_val(ncn->config, address))) + if(!(cfg = get_config_val(ncn->config, config_address))) { syslog(LOG_ERR, _("No address specified for %s")); free_conn_list(ncn); @@ -678,7 +800,6 @@ cp ncn->buffer = xmalloc(MAXBUFSIZE); ncn->buflen = 0; ncn->last_ping_time = time(NULL); - ncn->want_ping = 0; conn_list_add(ncn); @@ -693,6 +814,7 @@ cp int setup_myself(void) { config_t const *cfg; + config_t *next; subnet_t *net; cp myself = new_conn_list(); @@ -701,7 +823,7 @@ cp myself->flags = 0; myself->protocol_version = PROT_CURRENT; - if(!(cfg = get_config_val(config, tincname))) /* Not acceptable */ + if(!(cfg = get_config_val(config, config_name))) /* Not acceptable */ { syslog(LOG_ERR, _("Name for tinc daemon required!")); return -1; @@ -715,7 +837,7 @@ cp return -1; } cp - if(!(cfg = get_config_val(config, privatekey))) + if(!(cfg = get_config_val(config, config_privatekey))) { syslog(LOG_ERR, _("Private key for tinc daemon required!")); return -1; @@ -733,7 +855,7 @@ cp return -1; } cp - if(!(cfg = get_config_val(myself->config, publickey))) + if(!(cfg = get_config_val(myself->config, config_publickey))) { syslog(LOG_ERR, _("Public key for tinc daemon required!")); return -1; @@ -749,22 +871,22 @@ cp return -1; } */ - if(!(cfg = get_config_val(myself->config, port))) + if(!(cfg = get_config_val(myself->config, config_port))) myself->port = 655; else myself->port = cfg->data.val; - if((cfg = get_config_val(myself->config, indirectdata))) + if((cfg = get_config_val(myself->config, config_indirectdata))) if(cfg->data.val == stupid_true) myself->flags |= EXPORTINDIRECTDATA; - if((cfg = get_config_val(myself->config, tcponly))) + if((cfg = get_config_val(myself->config, config_tcponly))) if(cfg->data.val == stupid_true) myself->flags |= TCPONLY; /* Read in all the subnets specified in the host configuration file */ - for(cfg = myself->config; (cfg = get_config_val(cfg, subnet)); cfg = cfg->next) + for(next = myself->config; (cfg = get_config_val(next, config_subnet)); next = cfg->next) { net = new_subnet(); net->type = SUBNET_IPV4; @@ -788,13 +910,6 @@ cp return -1; } - if((myself->socket = setup_vpn_in_socket(myself->port)) < 0) - { - syslog(LOG_ERR, _("Unable to set up an incoming vpn data socket!")); - close(myself->meta_socket); - return -1; - } - /* Generate packet encryption key */ myself->cipher_pkttype = EVP_bf_cfb(); @@ -804,7 +919,7 @@ cp myself->cipher_pktkey = (char *)xmalloc(myself->cipher_pktkeylength); RAND_bytes(myself->cipher_pktkey, myself->cipher_pktkeylength); - if(!(cfg = get_config_val(config, keyexpire))) + if(!(cfg = get_config_val(config, config_keyexpire))) keylifetime = 3600; else keylifetime = cfg->data.val; @@ -816,6 +931,8 @@ cp myself->status.active = 1; syslog(LOG_NOTICE, _("Ready: listening on port %hd"), myself->port); + + child_pids = list_new(); cp return 0; } @@ -825,7 +942,7 @@ sigalrm_handler(int a) { config_t const *cfg; cp - cfg = get_config_val(upstreamcfg, connectto); + cfg = get_config_val(upstreamcfg, config_connectto); if(!cfg && upstreamcfg == config) /* No upstream IP given, we're listen only. */ @@ -839,7 +956,7 @@ cp signal(SIGALRM, SIG_IGN); return; } - cfg = get_config_val(upstreamcfg, connectto); /* Or else we try the next ConnectTo line */ + cfg = get_config_val(upstreamcfg, config_connectto); /* Or else we try the next ConnectTo line */ } signal(SIGALRM, sigalrm_handler); @@ -860,10 +977,16 @@ int setup_network_connections(void) { config_t const *cfg; cp - if((cfg = get_config_val(config, pingtimeout)) == NULL) - timeout = 5; + if((cfg = get_config_val(config, config_pingtimeout)) == NULL) + timeout = 60; else - timeout = cfg->data.val; + { + timeout = cfg->data.val; + if(timeout < 1) + { + timeout = 86400; + } + } if(setup_tap_fd() < 0) return -1; @@ -874,7 +997,7 @@ cp /* Run tinc-up script to further initialize the tap interface */ execute_script("tinc-up"); - if(!(cfg = get_config_val(config, connectto))) + if(!(cfg = get_config_val(config, config_connectto))) /* No upstream IP given, we're listen only. */ return 0; @@ -883,7 +1006,7 @@ cp upstreamcfg = cfg->next; if(!setup_outgoing_connection(cfg->data.ptr)) /* function returns 0 when there are no problems */ return 0; - cfg = get_config_val(upstreamcfg, connectto); /* Or else we try the next ConnectTo line */ + cfg = get_config_val(upstreamcfg, config_connectto); /* Or else we try the next ConnectTo line */ } signal(SIGALRM, sigalrm_handler); @@ -912,7 +1035,6 @@ cp if(myself->status.active) { close(myself->meta_socket); - close(myself->socket); free_conn_list(myself); myself = NULL; } @@ -936,6 +1058,7 @@ int setup_vpn_connection(conn_list_t *cl) { int nfd, flags; struct sockaddr_in a; + const int one = 1; cp if(debug_lvl >= DEBUG_TRAFFIC) syslog(LOG_DEBUG, _("Opening UDP socket to %s"), cl->hostname); @@ -947,12 +1070,42 @@ cp return -1; } + if(setsockopt(nfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) + { + close(nfd); + syslog(LOG_ERR, _("System call `%s' failed: %m"), + "setsockopt"); + return -1; + } + + flags = fcntl(nfd, F_GETFL); + if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0) + { + close(nfd); + syslog(LOG_ERR, _("System call `%s' failed: %m"), + "fcntl"); + return -1; + } + + memset(&a, 0, sizeof(a)); + a.sin_family = AF_INET; + a.sin_port = htons(myself->port); + a.sin_addr.s_addr = htonl(INADDR_ANY); + + if(bind(nfd, (struct sockaddr *)&a, sizeof(struct sockaddr))) + { + close(nfd); + syslog(LOG_ERR, _("Can't bind to port %hd/udp: %m"), myself->port); + return -1; + } + a.sin_family = AF_INET; a.sin_port = htons(cl->port); a.sin_addr.s_addr = htonl(cl->address); if(connect(nfd, (struct sockaddr *)&a, sizeof(a)) == -1) { + close(nfd); syslog(LOG_ERR, _("Connecting to %s port %d failed: %m"), cl->hostname, cl->port); return -1; @@ -961,6 +1114,7 @@ cp flags = fcntl(nfd, F_GETFL); if(fcntl(nfd, F_SETFL, flags | O_NONBLOCK) < 0) { + close(nfd); syslog(LOG_ERR, _("This is a bug: %s:%d: %d:%m %s (%s)"), __FILE__, __LINE__, nfd, cl->name, cl->hostname); return -1; @@ -984,7 +1138,7 @@ conn_list_t *create_new_connection(int sfd) cp p = new_conn_list(); - if(getpeername(sfd, &ci, &len) < 0) + if(getpeername(sfd, (struct sockaddr *) &ci, (socklen_t *) &len) < 0) { syslog(LOG_ERR, _("System call `%s' failed: %m"), "getpeername"); @@ -999,7 +1153,6 @@ cp p->buffer = xmalloc(MAXBUFSIZE); p->buflen = 0; p->last_ping_time = time(NULL); - p->want_ping = 0; if(debug_lvl >= DEBUG_CONNECTIONS) syslog(LOG_NOTICE, _("Connection from %s port %d"), @@ -1028,7 +1181,6 @@ cp } FD_SET(myself->meta_socket, fs); - FD_SET(myself->socket, fs); FD_SET(tap_fd, fs); cp } @@ -1038,18 +1190,16 @@ cp udp socket and write it to the ethertap device after being decrypted */ -int handle_incoming_vpn_data() +int handle_incoming_vpn_data(conn_list_t *cl) { vpn_packet_t pkt; int x, l = sizeof(x); - struct sockaddr from; int lenin; - socklen_t fromlen = sizeof(from); cp - if(getsockopt(myself->socket, SOL_SOCKET, SO_ERROR, &x, &l) < 0) + if(getsockopt(cl->socket, SOL_SOCKET, SO_ERROR, &x, &l) < 0) { syslog(LOG_ERR, _("This is a bug: %s:%d: %d:%m"), - __FILE__, __LINE__, myself->socket); + __FILE__, __LINE__, cl->socket); return -1; } if(x) @@ -1058,7 +1208,7 @@ cp return -1; } - if((lenin = recvfrom(myself->socket, (char *) &(pkt.len), MTU, 0, &from, &fromlen)) <= 0) + if((lenin = recv(cl->socket, (char *) &(pkt.len), MTU, 0)) <= 0) { syslog(LOG_ERR, _("Receiving packet failed: %m")); return -1; @@ -1066,11 +1216,12 @@ cp if(debug_lvl >= DEBUG_TRAFFIC) { - syslog(LOG_DEBUG, _("Received packet of %d bytes"), lenin); - } + syslog(LOG_DEBUG, _("Received packet of %d bytes from %s (%s)"), lenin, + cl->name, cl->hostname); + } cp - return xrecv(&pkt); + return xrecv(cl, &pkt); } /* @@ -1153,7 +1304,7 @@ cp { if(p->last_ping_time + timeout < now) { - if(p->status.pinged && !p->status.got_pong) + if(p->status.pinged) { if(debug_lvl >= DEBUG_PROTOCOL) syslog(LOG_INFO, _("%s (%s) didn't respond to PING"), @@ -1161,12 +1312,9 @@ cp p->status.timeout = 1; terminate_connection(p); } - else if(p->want_ping) + else { send_ping(p); - p->last_ping_time = now; - p->status.pinged = 1; - p->status.got_pong = 0; } } } @@ -1211,7 +1359,6 @@ cp void check_network_activity(fd_set *f) { conn_list_t *p; - int x, l = sizeof(x); cp for(p = conn_list; p != NULL; p = p->next) { @@ -1221,16 +1368,15 @@ cp if(p->status.dataopen) if(FD_ISSET(p->socket, f)) { - /* - The only thing that can happen to get us here is apparently an - error on this outgoing(!) UDP socket that isn't immediate (i.e. - something that will not trigger an error directly on send()). - I've once got here when it said `No route to host'. - */ + handle_incoming_vpn_data(p); + + /* Old error stuff (FIXME: copy this to handle_incoming_vpn_data() + getsockopt(p->socket, SOL_SOCKET, SO_ERROR, &x, &l); syslog(LOG_ERR, _("Outgoing data socket error for %s (%s): %s"), p->name, p->hostname, strerror(x)); terminate_connection(p); + */ return; } @@ -1243,9 +1389,6 @@ cp } } - if(FD_ISSET(myself->socket, f)) - handle_incoming_vpn_data(); - if(FD_ISSET(myself->meta_socket, f)) handle_new_meta_connection(); cp @@ -1378,6 +1521,8 @@ cp if(FD_ISSET(tap_fd, &fset)) handle_tap_input(); } + + check_children(); } cp }