+ // 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 */
+
+ TIME = now();
+ if (n < 0)
+ {
+ if (errno == EINTR ||
+ errno == ECHILD) // EINTR was clobbered by sigchild_handler()
+ continue;
+
+ LOG(0, 0, 0, 0, "Error returned from select(): %s\n", strerror(errno));
+ main_quit++;
+ break;
+ }
+ else if (n)
+ {
+ struct sockaddr_in addr;
+ int alen = sizeof(addr);
+ if (FD_ISSET(udpfd, &r))
+ {
+ int c, n;
+ for (c = 0; c < config->multi_read_count; c++)
+ {
+ if ((n = recvfrom(udpfd, buf, sizeof(buf), 0, (void *) &addr, &alen)) > 0)
+ processudp(buf, n, &addr);
+ else
+ break;
+ }
+ }
+ if (FD_ISSET(tunfd, &r))
+ {
+ int c, n;
+ for (c = 0; c < config->multi_read_count; c++)
+ {
+ if ((n = read(tunfd, buf, sizeof(buf))) > 0)
+ processtun(buf, n);
+ else
+ break;
+ }
+ }
+
+ 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);
+
+ if (FD_ISSET(cluster_sockfd, &r))
+ {
+ int size;
+ size = recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen);
+ processcluster(buf, size, addr.sin_addr.s_addr);
+ }
+
+ if (FD_ISSET(controlfd, &r))
+ processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr);
+
+ if (FD_ISSET(clifd, &r))
+ {
+ struct sockaddr_in addr;
+ int sockfd;
+ int len = sizeof(addr);
+
+ if ((sockfd = accept(clifd, (struct sockaddr *)&addr, &len)) <= 0)
+ {
+ LOG(0, 0, 0, 0, "accept error: %s\n", strerror(errno));
+ continue;
+ }
+ else
+ {
+ cli_do(sockfd);
+ close(sockfd);
+ }
+ }
+ }
+
+ // 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.
+ }
+ }
+
+#ifdef BGP
+ for (i = 0; i < BGP_NUM_PEERS; i++)
+ {
+ bgp_process(&bgp_peers[i],
+ bgp_set[i] ? FD_ISSET(bgp_peers[i].sock, &r) : 0,
+ bgp_set[i] ? FD_ISSET(bgp_peers[i].sock, &w) : 0);
+ }
+#endif /* BGP */
+ }
+
+ // 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
+void initdata(int optdebug, char *optconfig)
+{
+ int i;
+
+ if (!(_statistics = shared_malloc(sizeof(struct Tstats))))
+ {
+ LOG(0, 0, 0, 0, "Error doing malloc for _statistics: %s\n", strerror(errno));
+ exit(1);
+ }
+ if (!(config = shared_malloc(sizeof(struct configt))))
+ {
+ LOG(0, 0, 0, 0, "Error doing malloc for configuration: %s\n", strerror(errno));
+ exit(1);
+ }
+ memset(config, 0, sizeof(struct configt));
+ time(&config->start_time);
+ strncpy(config->config_file, optconfig, strlen(optconfig));
+ config->debug = optdebug;
+ config->num_tbfs = MAXTBFS;
+ config->rl_rate = 28; // 28kbps
+
+ if (!(tunnel = shared_malloc(sizeof(tunnelt) * MAXTUNNEL)))
+ {
+ LOG(0, 0, 0, 0, "Error doing malloc for tunnels: %s\n", strerror(errno));
+ exit(1);
+ }
+ if (!(session = shared_malloc(sizeof(sessiont) * MAXSESSION)))
+ {
+ LOG(0, 0, 0, 0, "Error doing malloc for sessions: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (!(sess_count = shared_malloc(sizeof(sessioncountt) * MAXSESSION)))
+ {
+ LOG(0, 0, 0, 0, "Error doing malloc for sessions_count: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (!(radius = shared_malloc(sizeof(radiust) * MAXRADIUS)))
+ {
+ LOG(0, 0, 0, 0, "Error doing malloc for radius: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (!(ip_address_pool = shared_malloc(sizeof(ippoolt) * MAXIPPOOL)))
+ {
+ LOG(0, 0, 0, 0, "Error doing malloc for ip_address_pool: %s\n", strerror(errno));
+ exit(1);
+ }
+
+#ifdef RINGBUFFER
+ if (!(ringbuffer = shared_malloc(sizeof(struct Tringbuffer))))
+ {
+ LOG(0, 0, 0, 0, "Error doing malloc for ringbuffer: %s\n", strerror(errno));
+ exit(1);
+ }
+ memset(ringbuffer, 0, sizeof(struct Tringbuffer));
+#endif
+
+ if (!(cli_session_actions = shared_malloc(sizeof(struct cli_session_actions) * MAXSESSION)))
+ {
+ LOG(0, 0, 0, 0, "Error doing malloc for cli session actions: %s\n", strerror(errno));
+ exit(1);
+ }
+ memset(cli_session_actions, 0, sizeof(struct cli_session_actions) * MAXSESSION);
+
+ if (!(cli_tunnel_actions = shared_malloc(sizeof(struct cli_tunnel_actions) * MAXSESSION)))
+ {
+ LOG(0, 0, 0, 0, "Error doing malloc for cli tunnel actions: %s\n", strerror(errno));
+ exit(1);
+ }
+ memset(cli_tunnel_actions, 0, sizeof(struct cli_tunnel_actions) * MAXSESSION);
+
+ memset(tunnel, 0, sizeof(tunnelt) * MAXTUNNEL);
+ memset(session, 0, sizeof(sessiont) * MAXSESSION);
+ memset(radius, 0, sizeof(radiust) * MAXRADIUS);
+ memset(ip_address_pool, 0, sizeof(ippoolt) * MAXIPPOOL);
+
+ // Put all the sessions on the free list marked as undefined.
+ for (i = 1; i < MAXSESSION - 1; i++)
+ {
+ session[i].next = i + 1;
+ session[i].tunnel = T_UNDEF; // mark it as not filled in.
+ }
+ session[MAXSESSION - 1].next = 0;
+ sessionfree = 1;
+
+ // Mark all the tunnels as undefined (waiting to be filled in by a download).
+ for (i = 1; i < MAXTUNNEL- 1; i++)
+ tunnel[i].state = TUNNELUNDEF; // mark it as not filled in.
+
+ if (!*hostname)
+ {
+ // Grab my hostname unless it's been specified
+ gethostname(hostname, sizeof(hostname));
+ stripdomain(hostname);
+ }
+
+ _statistics->start_time = _statistics->last_reset = time(NULL);
+
+#ifdef BGP
+ if (!(bgp_peers = shared_malloc(sizeof(struct bgp_peer) * BGP_NUM_PEERS)))
+ {
+ LOG(0, 0, 0, 0, "Error doing malloc for bgp: %s\n", strerror(errno));
+ exit(1);
+ }
+#endif /* BGP */
+}
+
+void initiptables(void)
+{
+ /* Flush the tables here so that we have a clean slate */
+
+// Not needed. 'nat' is setup by garden.c
+// mangle isn't used (as throttling is done by tbf inhouse).
+}
+
+int assign_ip_address(sessionidt s)
+{
+ u32 i;
+ int best = -1;
+ time_t best_time = time_now;
+ char *u = session[s].user;
+ char reuse = 0;
+
+
+ CSTAT(call_assign_ip_address);
+
+ for (i = 1; i < ip_pool_size; i++)
+ {
+ if (!ip_address_pool[i].address || ip_address_pool[i].assigned)
+ continue;
+
+ if (!session[s].walled_garden && ip_address_pool[i].user[0] && !strcmp(u, ip_address_pool[i].user))
+ {
+ best = i;
+ reuse = 1;
+ break;