+ if (!tunnel[i].controlc)
+ continue;
+
+ if (last_talked != TIME)
+ {
+ LOG(2, 0, 0, "Tunnel %d still has un-acked control messages.\n", i);
+ last_talked = TIME;
+ }
+ return 1;
+ }
+
+ // We stop waiting for radius after BUSY_WAIT_TIME 1/10th seconds
+ if (abs(TIME - start_busy_wait) > BUSY_WAIT_TIME)
+ {
+ LOG(1, 0, 0, "Giving up waiting for RADIUS to be empty. Shutting down anyway.\n");
+ return 0;
+ }
+
+ for (i = 1; i < MAXRADIUS; i++)
+ {
+ if (radius[i].state == RADIUSNULL)
+ continue;
+ if (radius[i].state == RADIUSWAIT)
+ continue;
+
+ if (last_talked != TIME)
+ {
+ LOG(2, 0, 0, "Radius session %d is still busy (sid %d)\n", i, radius[i].session);
+ last_talked = TIME;
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+static fd_set readset;
+static int readset_n = 0;
+
+// main loop - gets packets on tun or udp and processes them
+static void mainloop(void)
+{
+ int i;
+ uint8_t buf[65536];
+ struct timeval to;
+ clockt next_cluster_ping = 0; // send initial ping immediately
+ time_t next_clean = time_now + config->cleanup_interval;
+
+ LOG(4, 0, 0, "Beginning of main loop. udpfd=%d, tunfd=%d, cluster_sockfd=%d, controlfd=%d\n",
+ udpfd, tunfd, cluster_sockfd, controlfd);
+
+ FD_ZERO(&readset);
+ FD_SET(udpfd, &readset);
+ FD_SET(tunfd, &readset);
+ FD_SET(controlfd, &readset);
+ FD_SET(clifd, &readset);
+ if (cluster_sockfd) FD_SET(cluster_sockfd, &readset);
+ readset_n = udpfd;
+ if (tunfd > readset_n) readset_n = tunfd;
+ if (controlfd > readset_n) readset_n = controlfd;
+ if (clifd > readset_n) readset_n = clifd;
+ if (cluster_sockfd > readset_n) readset_n = cluster_sockfd;
+
+ while (!main_quit || still_busy())
+ {
+ fd_set r;
+ int n = readset_n;
+#ifdef BGP
+ fd_set w;
+ int bgp_set[BGP_NUM_PEERS];
+#endif /* BGP */
+
+ if (config->reload_config)
+ {
+ // Update the config state based on config settings
+ update_config();
+ }
+
+ memcpy(&r, &readset, sizeof(fd_set));
+ to.tv_sec = 0;
+ to.tv_usec = 100000; // 1/10th of a second.
+
+#ifdef BGP
+ FD_ZERO(&w);
+ for (i = 0; i < BGP_NUM_PEERS; i++)
+ {
+ bgp_set[i] = bgp_select_state(&bgp_peers[i]);
+ if (bgp_set[i] & 1)
+ {
+ FD_SET(bgp_peers[i].sock, &r);
+ if (bgp_peers[i].sock > n)
+ n = bgp_peers[i].sock;
+ }
+
+ if (bgp_set[i] & 2)
+ {
+ FD_SET(bgp_peers[i].sock, &w);
+ if (bgp_peers[i].sock > n)
+ n = bgp_peers[i].sock;
+ }
+ }
+
+ n = select(n + 1, &r, &w, 0, &to);
+#else /* BGP */
+ n = select(n + 1, &r, 0, 0, &to);
+#endif /* BGP */
+
+ STAT(select_called);
+
+ TIME = now();
+ if (n < 0)
+ {
+ if (errno == EINTR ||
+ errno == ECHILD) // EINTR was clobbered by sigchild_handler()
+ continue;
+
+ LOG(0, 0, 0, "Error returned from select(): %s\n", strerror(errno));
+ main_quit++;
+ break;
+ }
+ else if (n)
+ {
+ struct sockaddr_in addr;
+ int alen, c, s;
+ int udp_pkts = 0;
+ int tun_pkts = 0;
+ int cluster_pkts = 0;
+
+ // nsctl commands
+ if (FD_ISSET(controlfd, &r))
+ {
+ alen = sizeof(addr);
+ processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr, alen);
+ n--;
+ }
+
+ // RADIUS responses
+ if (config->cluster_iam_master)
+ {
+ for (i = 0; i < config->num_radfds; i++)
+ {
+ if (FD_ISSET(radfds[i], &r))
+ {
+ processrad(buf, recv(radfds[i], buf, sizeof(buf), 0), i);
+ n--;
+ }
+ }
+ }
+
+ // CLI connections
+ if (FD_ISSET(clifd, &r))
+ {
+ int cli;
+
+ alen = sizeof(addr);
+ if ((cli = accept(clifd, (struct sockaddr *)&addr, &alen)) >= 0)
+ {
+ cli_do(cli);
+ close(cli);
+ }
+ else
+ LOG(0, 0, 0, "accept error: %s\n", strerror(errno));
+
+ n--;
+ }
+
+#ifdef BGP
+ for (i = 0; i < BGP_NUM_PEERS; i++)
+ {
+ int isr = bgp_set[i] ? FD_ISSET(bgp_peers[i].sock, &r) : 0;
+ int isw = bgp_set[i] ? FD_ISSET(bgp_peers[i].sock, &w) : 0;
+ bgp_process(&bgp_peers[i], isr, isw);
+ if (isr) n--;
+ if (isw) n--;
+ }
+#endif /* BGP */
+
+ for (c = 0; n && c < config->multi_read_count; c++)
+ {
+ // L2TP
+ if (FD_ISSET(udpfd, &r))
+ {
+ alen = sizeof(addr);
+ if ((s = recvfrom(udpfd, buf, sizeof(buf), 0, (void *) &addr, &alen)) > 0)
+ {
+ processudp(buf, s, &addr);
+ udp_pkts++;
+ }
+ else
+ {
+ FD_CLR(udpfd, &r);
+ n--;
+ }
+ }
+
+ // incoming IP
+ if (FD_ISSET(tunfd, &r))
+ {
+ if ((s = read(tunfd, buf, sizeof(buf))) > 0)
+ {
+ processtun(buf, s);
+ tun_pkts++;
+ }
+ else
+ {
+ FD_CLR(tunfd, &r);
+ n--;
+ }
+ }
+
+ // cluster
+ if (FD_ISSET(cluster_sockfd, &r))
+ {
+ alen = sizeof(addr);
+ if ((s = recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen)) > 0)
+ {
+ processcluster(buf, s, addr.sin_addr.s_addr);
+ cluster_pkts++;
+ }
+ else
+ {
+ FD_CLR(cluster_sockfd, &r);
+ n--;
+ }
+ }
+ }
+
+ if (udp_pkts > 1 || tun_pkts > 1 || cluster_pkts > 1)
+ STAT(multi_read_used);
+
+ if (c >= config->multi_read_count)
+ {
+ LOG(3, 0, 0, "Reached multi_read_count (%d); processed %d udp, %d tun and %d cluster packets\n",
+ config->multi_read_count, udp_pkts, tun_pkts, cluster_pkts);
+
+ STAT(multi_read_exceeded);
+ }
+ }
+
+ // Runs on every machine (master and slaves).
+ if (cluster_sockfd && next_cluster_ping <= TIME)
+ {
+ // Check to see which of the cluster is still alive..
+
+ cluster_send_ping(basetime); // Only does anything if we're a slave
+ cluster_check_master(); // ditto.
+
+ cluster_heartbeat(); // Only does anything if we're a master.
+ cluster_check_slaves(); // ditto.
+
+ master_update_counts(); // If we're a slave, send our byte counters to our master.
+
+ if (config->cluster_iam_master && !config->cluster_iam_uptodate)
+ next_cluster_ping = TIME + 1; // out-of-date slaves, do fast updates
+ else
+ next_cluster_ping = TIME + config->cluster_hb_interval;
+ }
+
+ // Run token bucket filtering queue..
+ // Only run it every 1/10th of a second.
+ // Runs on all machines both master and slave.
+ {
+ static clockt last_run = 0;
+ if (last_run != TIME)
+ {
+ last_run = TIME;
+ tbf_run_timer();
+ }
+ }
+
+ /* Handle timeouts. Make sure that this gets run anyway, even if there was
+ * something to read, else under load this will never actually run....
+ *
+ */
+ if (config->cluster_iam_master && next_clean <= time_now)
+ {
+ if (regular_cleanups())
+ {
+ // Did it finish?
+ next_clean = time_now + 1 ; // Didn't finish. Check quickly.
+ }
+ else
+ {
+ next_clean = time_now + config->cleanup_interval; // Did. Move to next interval.
+ }
+ }
+ }
+
+ // Are we the master and shutting down??
+ if (config->cluster_iam_master)
+ cluster_heartbeat(); // Flush any queued changes..
+
+ // Ok. Notify everyone we're shutting down. If we're
+ // the master, this will force an election.
+ cluster_send_ping(0);
+
+ //
+ // Important!!! We MUST not process any packets past this point!
+}
+
+static void stripdomain(char *host)
+{
+ char *p;
+
+ if ((p = strchr(host, '.')))
+ {
+ char *domain = 0;
+ char _domain[1024];
+
+ // strip off domain
+ FILE *resolv = fopen("/etc/resolv.conf", "r");
+ if (resolv)
+ {
+ char buf[1024];
+ char *b;
+
+ while (fgets(buf, sizeof(buf), resolv))
+ {
+ if (strncmp(buf, "domain", 6) && strncmp(buf, "search", 6))
+ continue;
+
+ if (!isspace(buf[6]))
+ continue;
+
+ b = buf + 7;
+ while (isspace(*b)) b++;
+
+ if (*b)
+ {
+ char *d = b;
+ while (*b && !isspace(*b)) b++;
+ *b = 0;
+ if (buf[0] == 'd') // domain is canonical
+ {
+ domain = d;
+ break;
+ }
+
+ // first search line
+ if (!domain)
+ {
+ // hold, may be subsequent domain line
+ strncpy(_domain, d, sizeof(_domain))[sizeof(_domain)-1] = 0;
+ domain = _domain;
+ }
+ }
+ }
+
+ fclose(resolv);
+ }
+
+ if (domain)
+ {
+ int hl = strlen(host);
+ int dl = strlen(domain);
+ if (dl < hl && host[hl - dl - 1] == '.' && !strcmp(host + hl - dl, domain))
+ host[hl -dl - 1] = 0;
+ }
+ else
+ {
+ *p = 0; // everything after first dot
+ }
+ }
+}
+
+// Init data structures
+static void initdata(int optdebug, char *optconfig)
+{
+ int i;
+
+ if (!(config = shared_malloc(sizeof(configt))))