+ if (!(radius = shared_malloc(sizeof(radiust) * MAXRADIUS)))
+ {
+ LOG(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, "Error doing malloc for ip_address_pool: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (!(ip_filters = shared_malloc(sizeof(ip_filtert) * MAXFILTER)))
+ {
+ LOG(0, 0, 0, "Error doing malloc for ip_filters: %s\n", strerror(errno));
+ exit(1);
+ }
+ memset(ip_filters, 0, sizeof(ip_filtert) * MAXFILTER);
+
+ if (!(cli_session_actions = shared_malloc(sizeof(struct cli_session_actions) * MAXSESSION)))
+ {
+ LOG(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, "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; 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; 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, "Error doing malloc for bgp: %s\n", strerror(errno));
+ exit(1);
+ }
+#endif /* BGP */
+}
+
+static int assign_ip_address(sessionidt s)
+{
+ uint32_t i;
+ int best = -1;
+ time_t best_time = time_now;
+ char *u = session[s].user;
+ char reuse = 0;
+
+
+ CSTAT(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;
+ }
+
+ if (ip_address_pool[i].last < best_time)
+ {
+ best = i;
+ if (!(best_time = ip_address_pool[i].last))
+ break; // never used, grab this one
+ }
+ }
+
+ if (best < 0)
+ {
+ LOG(0, s, session[s].tunnel, "assign_ip_address(): out of addresses\n");
+ return 0;
+ }
+
+ session[s].ip = ip_address_pool[best].address;
+ session[s].ip_pool_index = best;
+ ip_address_pool[best].assigned = 1;
+ ip_address_pool[best].last = time_now;
+ ip_address_pool[best].session = s;
+ if (session[s].walled_garden)
+ /* Don't track addresses of users in walled garden (note: this
+ means that their address isn't "sticky" even if they get
+ un-gardened). */
+ ip_address_pool[best].user[0] = 0;
+ else
+ strncpy(ip_address_pool[best].user, u, sizeof(ip_address_pool[best].user) - 1);
+
+ STAT(ip_allocated);
+ LOG(4, s, session[s].tunnel, "assign_ip_address(): %s ip address %d from pool\n",
+ reuse ? "Reusing" : "Allocating", best);
+
+ return 1;
+}
+
+static void free_ip_address(sessionidt s)
+{
+ int i = session[s].ip_pool_index;
+
+
+ CSTAT(free_ip_address);
+
+ if (!session[s].ip)
+ return; // what the?
+
+ if (i < 0) // Is this actually part of the ip pool?
+ i = 0;
+
+ STAT(ip_freed);
+ cache_ipmap(session[s].ip, -i); // Change the mapping to point back to the ip pool index.
+ session[s].ip = 0;
+ ip_address_pool[i].assigned = 0;
+ ip_address_pool[i].session = 0;
+ ip_address_pool[i].last = time_now;
+}
+
+//
+// Fsck the address pool against the session table.
+// Normally only called when we become a master.
+//
+// This isn't perfect: We aren't keep tracking of which
+// users used to have an IP address.
+//
+void rebuild_address_pool(void)
+{
+ int i;
+
+ //
+ // Zero the IP pool allocation, and build
+ // a map from IP address to pool index.
+ for (i = 1; i < MAXIPPOOL; ++i)
+ {
+ ip_address_pool[i].assigned = 0;
+ ip_address_pool[i].session = 0;
+ if (!ip_address_pool[i].address)
+ continue;
+
+ cache_ipmap(ip_address_pool[i].address, -i); // Map pool IP to pool index.
+ }
+
+ for (i = 0; i < MAXSESSION; ++i)
+ {
+ int ipid;
+ if (!(session[i].opened && session[i].ip))
+ continue;
+
+ ipid = - lookup_ipmap(htonl(session[i].ip));
+
+ if (session[i].ip_pool_index < 0)
+ {
+ // Not allocated out of the pool.
+ if (ipid < 1) // Not found in the pool either? good.
+ continue;
+
+ LOG(0, i, 0, "Session %d has an IP address (%s) that was marked static, but is in the pool (%d)!\n",
+ i, fmtaddr(session[i].ip, 0), ipid);
+
+ // Fall through and process it as part of the pool.
+ }
+
+
+ if (ipid > MAXIPPOOL || ipid < 0)
+ {
+ LOG(0, i, 0, "Session %d has a pool IP that's not found in the pool! (%d)\n", i, ipid);
+ ipid = -1;
+ session[i].ip_pool_index = ipid;
+ continue;
+ }
+
+ ip_address_pool[ipid].assigned = 1;
+ ip_address_pool[ipid].session = i;
+ ip_address_pool[ipid].last = time_now;
+ strncpy(ip_address_pool[ipid].user, session[i].user, sizeof(ip_address_pool[ipid].user) - 1);
+ session[i].ip_pool_index = ipid;
+ cache_ipmap(session[i].ip, i); // Fix the ip map.
+ }
+}
+
+//
+// Fix the address pool to match a changed session.
+// (usually when the master sends us an update).
+static void fix_address_pool(int sid)
+{
+ int ipid;
+
+ ipid = session[sid].ip_pool_index;
+
+ if (ipid > ip_pool_size)
+ return; // Ignore it. rebuild_address_pool will fix it up.
+
+ if (ip_address_pool[ipid].address != session[sid].ip)
+ return; // Just ignore it. rebuild_address_pool will take care of it.
+
+ ip_address_pool[ipid].assigned = 1;
+ ip_address_pool[ipid].session = sid;
+ ip_address_pool[ipid].last = time_now;
+ strncpy(ip_address_pool[ipid].user, session[sid].user, sizeof(ip_address_pool[ipid].user) - 1);
+}
+
+//
+// Add a block of addresses to the IP pool to hand out.
+//
+static void add_to_ip_pool(in_addr_t addr, in_addr_t mask)
+{
+ int i;
+ if (mask == 0)
+ mask = 0xffffffff; // Host route only.
+
+ addr &= mask;
+
+ if (ip_pool_size >= MAXIPPOOL) // Pool is full!
+ return ;
+
+ for (i = addr ;(i & mask) == addr; ++i)
+ {
+ if ((i & 0xff) == 0 || (i&0xff) == 255)
+ continue; // Skip 0 and broadcast addresses.
+
+ ip_address_pool[ip_pool_size].address = i;
+ ip_address_pool[ip_pool_size].assigned = 0;
+ ++ip_pool_size;
+ if (ip_pool_size >= MAXIPPOOL)
+ {
+ LOG(0, 0, 0, "Overflowed IP pool adding %s\n", fmtaddr(htonl(addr), 0));
+ return;
+ }
+ }
+}
+
+// Initialize the IP address pool
+static void initippool()
+{
+ FILE *f;
+ char *p;
+ char buf[4096];
+ memset(ip_address_pool, 0, sizeof(ip_address_pool));
+
+ if (!(f = fopen(IPPOOLFILE, "r")))
+ {
+ LOG(0, 0, 0, "Can't load pool file " IPPOOLFILE ": %s\n", strerror(errno));
+ exit(1);
+ }
+
+ while (ip_pool_size < MAXIPPOOL && fgets(buf, 4096, f))
+ {
+ char *pool = buf;
+ buf[4095] = 0; // Force it to be zero terminated/
+
+ if (*buf == '#' || *buf == '\n')
+ continue; // Skip comments / blank lines
+ if ((p = (char *)strrchr(buf, '\n'))) *p = 0;
+ if ((p = (char *)strchr(buf, ':')))
+ {
+ in_addr_t src;
+ *p = '\0';
+ src = inet_addr(buf);
+ if (src == INADDR_NONE)
+ {
+ LOG(0, 0, 0, "Invalid address pool IP %s\n", buf);
+ exit(1);
+ }
+ // This entry is for a specific IP only
+ if (src != config->bind_address)
+ continue;
+ *p = ':';
+ pool = p+1;
+ }
+ if ((p = (char *)strchr(pool, '/')))
+ {
+ // It's a range
+ int numbits = 0;
+ in_addr_t start = 0, mask = 0;
+
+ LOG(2, 0, 0, "Adding IP address range %s\n", buf);
+ *p++ = 0;
+ if (!*p || !(numbits = atoi(p)))
+ {
+ LOG(0, 0, 0, "Invalid pool range %s\n", buf);
+ continue;
+ }
+ start = ntohl(inet_addr(pool));
+ mask = (in_addr_t) (pow(2, numbits) - 1) << (32 - numbits);
+
+ // Add a static route for this pool
+ LOG(5, 0, 0, "Adding route for address pool %s/%u\n",
+ fmtaddr(htonl(start), 0), 32 + mask);
+
+ routeset(0, start, mask, 0, 1);
+
+ add_to_ip_pool(start, mask);
+ }
+ else
+ {
+ // It's a single ip address
+ add_to_ip_pool(inet_addr(pool), 0);
+ }
+ }
+ fclose(f);
+ LOG(1, 0, 0, "IP address pool is %d addresses\n", ip_pool_size - 1);
+}
+
+void snoop_send_packet(uint8_t *packet, uint16_t size, in_addr_t destination, uint16_t port)
+{
+ struct sockaddr_in snoop_addr = {0};
+ if (!destination || !port || snoopfd <= 0 || size <= 0 || !packet)
+ return;
+
+ snoop_addr.sin_family = AF_INET;
+ snoop_addr.sin_addr.s_addr = destination;
+ snoop_addr.sin_port = ntohs(port);
+
+ LOG(5, 0, 0, "Snooping %d byte packet to %s:%d\n", size,
+ fmtaddr(snoop_addr.sin_addr.s_addr, 0),
+ htons(snoop_addr.sin_port));
+
+ if (sendto(snoopfd, packet, size, MSG_DONTWAIT | MSG_NOSIGNAL, (void *) &snoop_addr, sizeof(snoop_addr)) < 0)
+ LOG(0, 0, 0, "Error sending intercept packet: %s\n", strerror(errno));
+
+ STAT(packets_snooped);
+}
+
+static int dump_session(FILE **f, sessiont *s)
+{
+ if (!s->opened || !s->ip || !(s->cin_delta || s->cout_delta) || !*s->user || s->walled_garden)
+ return 1;
+
+ if (!*f)
+ {
+ char filename[1024];
+ char timestr[64];
+ time_t now = time(NULL);
+
+ strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S", localtime(&now));
+ snprintf(filename, sizeof(filename), "%s/%s", config->accounting_dir, timestr);
+
+ if (!(*f = fopen(filename, "w")))
+ {
+ LOG(0, 0, 0, "Can't write accounting info to %s: %s\n", filename, strerror(errno));
+ return 0;
+ }
+
+ LOG(3, 0, 0, "Dumping accounting information to %s\n", filename);
+ fprintf(*f, "# dslwatch.pl dump file V1.01\n"
+ "# host: %s\n"
+ "# endpoint: %s\n"
+ "# time: %ld\n"
+ "# uptime: %ld\n"
+ "# format: username ip qos uptxoctets downrxoctets\n",
+ hostname,
+ fmtaddr(config->bind_address ? config->bind_address : my_address, 0),
+ now,
+ now - basetime);
+ }
+
+ LOG(4, 0, 0, "Dumping accounting information for %s\n", s->user);
+ fprintf(*f, "%s %s %d %u %u\n",
+ s->user, // username
+ fmtaddr(htonl(s->ip), 0), // ip
+ (s->throttle_in || s->throttle_out) ? 2 : 1, // qos
+ (uint32_t) s->cin_delta, // uptxoctets
+ (uint32_t) s->cout_delta); // downrxoctets
+
+ s->cin_delta = s->cout_delta = 0;
+
+ return 1;
+}
+
+static void dump_acct_info(int all)
+{
+ int i;
+ FILE *f = NULL;
+
+
+ CSTAT(dump_acct_info);
+
+ if (shut_acct_n)