-/*
- dispatch any incoming meta requests
-*/
-int handle_incoming_meta_data(conn_list_t *cl)
-{
- int x, l = sizeof(x), lenin;
- unsigned char tmp[1600];
- int request;
-
- if(getsockopt(cl->meta_socket, SOL_SOCKET, SO_ERROR, &x, &l) < 0)
- {
- syslog(LOG_ERR, "This is a bug: %s:%d: %d:%m", __FILE__, __LINE__, cl->meta_socket);
- return -1;
- }
- if(x)
- {
- syslog(LOG_ERR, "Metadata socket error: %s", sys_errlist[x]);
- return -1;
- }
-
- if((lenin = recv(cl->meta_socket, &tmp, sizeof(tmp), 0)) <= 0)
- {
- syslog(LOG_ERR, "Receive failed: %m");
- return -1;
- }
-
- request = (int)(tmp[0]);
-
- if(debug_lvl > 3)
- syslog(LOG_DEBUG, "got request %d", request);
-
- if(request_handlers[request] == NULL)
- syslog(LOG_ERR, "Unknown request %d.", request);
- else
- if(request_handlers[request](cl, tmp, lenin) < 0)
- return -1;
-
- return 0;
-}
-
-/*
- check all connections to see if anything
- happened on their sockets
-*/
-void check_network_activity(fd_set *f)
-{
- conn_list_t *p;
- int x, l = sizeof(x);
-
- for(p = conn_list; p != NULL; p = p->next)
- {
- if(p->status.remove)
- continue;
-cp
- if(p->status.active)
- 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'.
- */
- getsockopt(p->socket, SOL_SOCKET, SO_ERROR, &x, &l);
- syslog(LOG_ERR, "Outgoing data socket error: %s", sys_errlist[x]);
- terminate_connection(p);
- return;
- }
-cp
- if(p->status.meta)
- if(FD_ISSET(p->meta_socket, f))
- if(handle_incoming_meta_data(p) < 0)
- {
- terminate_connection(p);
- return;
- }
-cp
- }
-
-cp
- if(FD_ISSET(myself->socket, f))
- handle_incoming_vpn_data(myself);
-cp
- if(FD_ISSET(myself->meta_socket, f))
- handle_new_meta_connection(myself);
-cp
-}
-
-/*
- read, encrypt and send data that is
- available through the ethertap device
-*/
-void handle_tap_input(void)
-{
- vpn_packet_t vp;
- ip_t from, to;
- int ether_type, lenin;
-
- memset(&vp, 0, sizeof(vp));
- if((lenin = read(tap_fd, &vp, MTU)) <= 0)
- {
- syslog(LOG_ERR, "Error while reading from tapdevice: %m");
- return;
- }
-
- total_tap_in += lenin;
-
- ether_type = ntohs(*((unsigned short*)(&vp.data[12])));
- if(ether_type != 0x0800)
- {
- if(debug_lvl > 0)
- syslog(LOG_INFO, "Non-IP ethernet frame %04x from " MAC_ADDR_S,
- ether_type, MAC_ADDR_V(vp.data[6]));
- return;
- }
-
- if(lenin < 32)
- {
- if(debug_lvl > 0)
- syslog(LOG_INFO, "Dropping short packet");
- return;
- }
-
- from = ntohl(*((unsigned long*)(&vp.data[26])));
- to = ntohl(*((unsigned long*)(&vp.data[30])));
-
- if(debug_lvl > 2)
- syslog(LOG_DEBUG, "An IP packet (%04x) for " IP_ADDR_S " from " IP_ADDR_S,
- ether_type, IP_ADDR_V(to), IP_ADDR_V(from));
- if(debug_lvl > 3)
- syslog(LOG_DEBUG, MAC_ADDR_S " to " MAC_ADDR_S,
- MAC_ADDR_V(vp.data[0]), MAC_ADDR_V(vp.data[6]));
-
- vp.len = (length_t)lenin - 2;
-cp
- strip_mac_addresses(&vp);
-cp
- send_packet(to, &vp);
-cp
-}
-
-/*
- this is where it al happens...
-*/
-void main_loop(void)
-{
- fd_set fset;
- struct timeval tv;
- int r;
-
- last_ping_time = time(NULL);
-
- for(;;)
- {
- tv.tv_sec = timeout;
- tv.tv_usec = 0;
-
-cp
- prune_conn_list();
-cp
- build_fdset(&fset);
-cp
-
- if((r = select(FD_SETSIZE, &fset, NULL, NULL, &tv)) < 0)
- {
- if(errno == EINTR) /* because of alarm */
- continue;
- syslog(LOG_ERR, "Error while waiting for input: %m");
- return;
- }
-cp
-
- if(r == 0 || last_ping_time + timeout < time(NULL))
- /* Timeout... hm... something might be wrong. */
- {
- check_dead_connections();
- send_broadcast_ping();
- continue;
+ cp();
+
+ last_ping_check = now;
+ last_config_check = now;
+ srand(now);
+
+ running = true;
+
+ while(running) {
+ now = time(NULL);
+
+ tv.tv_sec = 1 + (rand() & 7); /* Approx. 5 seconds, randomized to prevent global synchronisation effects */
+ tv.tv_usec = 0;
+
+ maxfd = build_fdset(&fset);
+
+ r = select(maxfd + 1, &fset, NULL, NULL, &tv);
+
+ if(r < 0) {
+ if(errno != EINTR && errno != EAGAIN) {
+ logger(LOG_ERR, _("Error while waiting for input: %s"),
+ strerror(errno));
+ cp_trace();
+ dump_connections();
+ return 1;
+ }
+
+ continue;
+ }
+
+ check_network_activity(&fset);
+
+ if(do_purge) {
+ purge();
+ do_purge = false;
+ }
+
+ /* Let's check if everybody is still alive */
+
+ if(last_ping_check + pingtimeout < now) {
+ check_dead_connections();
+ last_ping_check = now;
+
+ if(routing_mode == RMODE_SWITCH)
+ age_mac();
+
+ age_past_requests();
+
+ /* Should we regenerate our key? */
+
+ if(keyexpires < now) {
+ ifdebug(STATUS) logger(LOG_INFO, _("Regenerating symmetric key"));
+
+ RAND_pseudo_bytes(myself->key, myself->keylength);
+ if(myself->cipher)
+ EVP_DecryptInit_ex(&packet_ctx, myself->cipher, NULL, myself->key, myself->key + myself->cipher->key_len);
+ send_key_changed(broadcast, myself);
+ keyexpires = now + keylifetime;
+ }
+ }
+
+
+ while((event = get_expired_event())) {
+ event->handler(event->data);
+ free(event);
+ }
+
+ if(sigalrm) {
+ logger(LOG_INFO, _("Flushing event queue"));
+
+ while(event_tree->head) {
+ event = (event_t *) event_tree->head->data;
+ event->handler(event->data);
+ event_del(event);
+ }
+ sigalrm = false;
+ }
+
+ if(sighup) {
+ connection_t *c;
+ avl_node_t *node;
+ char *fname;
+ struct stat s;
+
+ sighup = false;
+
+ /* Reread our own configuration file */
+
+ exit_configuration(&config_tree);
+ init_configuration(&config_tree);
+
+ if(!read_server_config()) {
+ logger(LOG_ERR, _("Unable to reread configuration file, exitting."));
+ return 1;
+ }
+
+ /* Close connections to hosts that have a changed or deleted host config file */
+
+ for(node = connection_tree->head; node; node = node->next) {
+ c = (connection_t *) node->data;
+
+ if(c->outgoing) {
+ free(c->outgoing->name);
+ freeaddrinfo(c->outgoing->ai);
+ free(c->outgoing);
+ c->outgoing = NULL;
+ }
+
+ asprintf(&fname, "%s/hosts/%s", confbase, c->name);
+ if(stat(fname, &s) || s.st_mtime > last_config_check)
+ terminate_connection(c, c->status.active);
+ free(fname);
+ }
+
+ last_config_check = now;
+
+ /* Try to make outgoing connections */
+
+ try_outgoing_connections();
+ }