+ 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 %u 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 %u 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(ntohl(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:%u\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)
+ {
+ for (i = 0; i < shut_acct_n; i++)
+ dump_session(&f, &shut_acct[i]);
+
+ shut_acct_n = 0;
+ }
+
+ if (all)
+ for (i = 1; i <= config->cluster_highest_sessionid; i++)
+ dump_session(&f, &session[i]);
+
+ if (f)
+ fclose(f);
+}
+
+// Main program
+int main(int argc, char *argv[])
+{
+ int i;
+ int optdebug = 0;
+ char *optconfig = CONFIGFILE;
+
+ time(&basetime); // start clock
+
+ // scan args
+ while ((i = getopt(argc, argv, "dvc:h:")) >= 0)
+ {
+ switch (i)
+ {
+ case 'd':
+ if (fork()) exit(0);
+ setsid();
+ freopen("/dev/null", "r", stdin);
+ freopen("/dev/null", "w", stdout);
+ freopen("/dev/null", "w", stderr);
+ break;
+ case 'v':
+ optdebug++;
+ break;
+ case 'c':
+ optconfig = optarg;
+ break;
+ case 'h':
+ snprintf(hostname, sizeof(hostname), "%s", optarg);
+ break;
+ default:
+ printf("Args are:\n"
+ "\t-d\t\tDetach from terminal\n"
+ "\t-c <file>\tConfig file\n"
+ "\t-h <hostname>\tForce hostname\n"
+ "\t-v\t\tDebug\n");
+
+ return (0);
+ break;
+ }
+ }
+
+ // Start the timer routine off
+ time(&time_now);
+ strftime(time_now_string, sizeof(time_now_string), "%Y-%m-%d %H:%M:%S", localtime(&time_now));
+
+ initplugins();
+ initdata(optdebug, optconfig);
+
+ init_cli(hostname);
+ read_config_file();
+ update_config();
+ init_tbf(config->num_tbfs);
+
+ LOG(0, 0, 0, "L2TPNS version " VERSION "\n");
+ LOG(0, 0, 0, "Copyright (c) 2003, 2004, 2005, 2006 Optus Internet Engineering\n");
+ LOG(0, 0, 0, "Copyright (c) 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n");
+ {
+ struct rlimit rlim;
+ rlim.rlim_cur = RLIM_INFINITY;
+ rlim.rlim_max = RLIM_INFINITY;
+ // Remove the maximum core size
+ if (setrlimit(RLIMIT_CORE, &rlim) < 0)
+ LOG(0, 0, 0, "Can't set ulimit: %s\n", strerror(errno));
+
+ // Make core dumps go to /tmp
+ chdir("/tmp");
+ }
+
+ if (config->scheduler_fifo)
+ {
+ int ret;
+ struct sched_param params = {0};
+ params.sched_priority = 1;
+
+ if (get_nprocs() < 2)
+ {
+ LOG(0, 0, 0, "Not using FIFO scheduler, there is only 1 processor in the system.\n");
+ config->scheduler_fifo = 0;
+ }
+ else
+ {
+ if ((ret = sched_setscheduler(0, SCHED_FIFO, ¶ms)) == 0)
+ {
+ LOG(1, 0, 0, "Using FIFO scheduler. Say goodbye to any other processes running\n");
+ }
+ else
+ {
+ LOG(0, 0, 0, "Error setting scheduler to FIFO: %s\n", strerror(errno));
+ config->scheduler_fifo = 0;
+ }
+ }
+ }
+
+ /* Set up the cluster communications port. */
+ if (cluster_init() < 0)
+ exit(1);
+
+ inittun();
+ LOG(1, 0, 0, "Set up on interface %s\n", config->tundevice);
+
+ initudp();
+ initrad();
+ initippool();
+
+ // seed prng
+ {
+ unsigned seed = time_now ^ getpid();
+ LOG(4, 0, 0, "Seeding the pseudo random generator: %u\n", seed);
+ srand(seed);
+ }
+
+ signal(SIGHUP, sighup_handler);
+ signal(SIGCHLD, sigchild_handler);
+ signal(SIGTERM, shutdown_handler);
+ signal(SIGINT, shutdown_handler);
+ signal(SIGQUIT, shutdown_handler);
+
+ // Prevent us from getting paged out
+ if (config->lock_pages)
+ {
+ if (!mlockall(MCL_CURRENT))
+ LOG(1, 0, 0, "Locking pages into memory\n");
+ else
+ LOG(0, 0, 0, "Can't lock pages: %s\n", strerror(errno));
+ }
+
+ mainloop();
+
+ /* remove plugins (so cleanup code gets run) */
+ plugins_done();
+
+ // Remove the PID file if we wrote it
+ if (config->wrote_pid && *config->pid_file == '/')
+ unlink(config->pid_file);
+
+ /* kill CLI children */
+ signal(SIGTERM, SIG_IGN);
+ kill(0, SIGTERM);
+ return 0;
+}
+
+static void sighup_handler(int sig)
+{
+ main_reload++;
+}
+
+static void shutdown_handler(int sig)
+{
+ main_quit = (sig == SIGQUIT) ? QUIT_SHUTDOWN : QUIT_FAILOVER;
+}
+
+static void sigchild_handler(int sig)
+{
+ while (waitpid(-1, NULL, WNOHANG) > 0)
+ ;
+}
+
+static void build_chap_response(uint8_t *challenge, uint8_t id, uint16_t challenge_length, uint8_t **challenge_response)
+{
+ MD5_CTX ctx;
+ *challenge_response = NULL;
+
+ if (!*config->l2tp_secret)
+ {
+ LOG(0, 0, 0, "LNS requested CHAP authentication, but no l2tp secret is defined\n");
+ return;
+ }
+
+ LOG(4, 0, 0, " Building challenge response for CHAP request\n");
+
+ *challenge_response = calloc(17, 1);
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, &id, 1);
+ MD5_Update(&ctx, config->l2tp_secret, strlen(config->l2tp_secret));
+ MD5_Update(&ctx, challenge, challenge_length);
+ MD5_Final(*challenge_response, &ctx);
+
+ return;
+}
+
+static int facility_value(char *name)
+{
+ int i;
+ for (i = 0; facilitynames[i].c_name; i++)
+ {
+ if (strcmp(facilitynames[i].c_name, name) == 0)
+ return facilitynames[i].c_val;
+ }
+ return 0;
+}
+
+static void update_config()
+{
+ int i;
+ char *p;
+ static int timeout = 0;
+ static int interval = 0;
+
+ // Update logging
+ closelog();
+ syslog_log = 0;
+ if (log_stream)
+ {
+ if (log_stream != stderr)
+ fclose(log_stream);
+
+ log_stream = NULL;
+ }
+
+ if (*config->log_filename)
+ {
+ if (strstr(config->log_filename, "syslog:") == config->log_filename)
+ {
+ char *p = config->log_filename + 7;
+ if (*p)
+ {
+ openlog("l2tpns", LOG_PID, facility_value(p));
+ syslog_log = 1;
+ }
+ }
+ else if (strchr(config->log_filename, '/') == config->log_filename)
+ {
+ if ((log_stream = fopen((char *)(config->log_filename), "a")))
+ {
+ fseek(log_stream, 0, SEEK_END);
+ setbuf(log_stream, NULL);
+ }
+ else
+ {
+ log_stream = stderr;
+ setbuf(log_stream, NULL);
+ }
+ }
+ }
+ else
+ {
+ log_stream = stderr;
+ setbuf(log_stream, NULL);
+ }
+
+#define L2TP_HDRS (20+8+6+4) // L2TP data encaptulation: ip + udp + l2tp (data) + ppp (inc hdlc)
+#define TCP_HDRS (20+20) // TCP encapsulation: ip + tcp
+
+ if (config->l2tp_mtu <= 0) config->l2tp_mtu = 1500; // ethernet default
+ else if (config->l2tp_mtu < MINMTU) config->l2tp_mtu = MINMTU;
+ else if (config->l2tp_mtu > MAXMTU) config->l2tp_mtu = MAXMTU;
+
+ // reset MRU/MSS globals
+ MRU = config->l2tp_mtu - L2TP_HDRS;
+ if (MRU > PPPoE_MRU)
+ MRU = PPPoE_MRU;
+
+ MSS = MRU - TCP_HDRS;
+
+ // Update radius
+ config->numradiusservers = 0;
+ for (i = 0; i < MAXRADSERVER; i++)
+ if (config->radiusserver[i])
+ {
+ config->numradiusservers++;
+ // Set radius port: if not set, take the port from the
+ // first radius server. For the first radius server,
+ // take the #defined default value from l2tpns.h
+
+ // test twice, In case someone works with
+ // a secondary radius server without defining
+ // a primary one, this will work even then.
+ if (i > 0 && !config->radiusport[i])
+ config->radiusport[i] = config->radiusport[i-1];
+ if (!config->radiusport[i])
+ config->radiusport[i] = RADPORT;
+ }
+
+ if (!config->numradiusservers)
+ LOG(0, 0, 0, "No RADIUS servers defined!\n");
+
+ // parse radius_authtypes_s
+ config->radius_authtypes = config->radius_authprefer = 0;
+ p = config->radius_authtypes_s;
+ while (p && *p)
+ {
+ char *s = strpbrk(p, " \t,");
+ int type = 0;
+
+ if (s)
+ {
+ *s++ = 0;
+ while (*s == ' ' || *s == '\t')
+ s++;
+
+ if (!*s)
+ s = 0;
+ }
+
+ if (!strncasecmp("chap", p, strlen(p)))
+ type = AUTHCHAP;
+ else if (!strncasecmp("pap", p, strlen(p)))
+ type = AUTHPAP;
+ else
+ LOG(0, 0, 0, "Invalid RADIUS authentication type \"%s\"\n", p);
+
+ config->radius_authtypes |= type;
+ if (!config->radius_authprefer)
+ config->radius_authprefer = type;
+
+ p = s;
+ }
+
+ if (!config->radius_authtypes)
+ {
+ LOG(0, 0, 0, "Defaulting to PAP authentication\n");
+ config->radius_authtypes = config->radius_authprefer = AUTHPAP;
+ }
+
+ // normalise radius_authtypes_s
+ if (config->radius_authprefer == AUTHPAP)
+ {
+ strcpy(config->radius_authtypes_s, "pap");
+ if (config->radius_authtypes & AUTHCHAP)
+ strcat(config->radius_authtypes_s, ", chap");
+ }
+ else
+ {
+ strcpy(config->radius_authtypes_s, "chap");
+ if (config->radius_authtypes & AUTHPAP)
+ strcat(config->radius_authtypes_s, ", pap");
+ }
+
+ if (!config->radius_dae_port)
+ config->radius_dae_port = DAEPORT;
+
+ // re-initialise the random number source
+ initrandom(config->random_device);
+
+ // Update plugins
+ for (i = 0; i < MAXPLUGINS; i++)
+ {
+ if (strcmp(config->plugins[i], config->old_plugins[i]) == 0)
+ continue;
+
+ if (*config->plugins[i])
+ {
+ // Plugin added
+ add_plugin(config->plugins[i]);
+ }
+ else if (*config->old_plugins[i])
+ {
+ // Plugin removed
+ remove_plugin(config->old_plugins[i]);
+ }
+ }
+
+ memcpy(config->old_plugins, config->plugins, sizeof(config->plugins));
+ if (!config->multi_read_count) config->multi_read_count = 10;
+ if (!config->cluster_address) config->cluster_address = inet_addr(DEFAULT_MCAST_ADDR);
+ if (!*config->cluster_interface)
+ strncpy(config->cluster_interface, DEFAULT_MCAST_INTERFACE, sizeof(config->cluster_interface) - 1);
+
+ if (!config->cluster_hb_interval)
+ config->cluster_hb_interval = PING_INTERVAL; // Heartbeat every 0.5 seconds.
+
+ if (!config->cluster_hb_timeout)
+ config->cluster_hb_timeout = HB_TIMEOUT; // 10 missed heartbeat triggers an election.
+
+ if (interval != config->cluster_hb_interval || timeout != config->cluster_hb_timeout)
+ {
+ // Paranoia: cluster_check_master() treats 2 x interval + 1 sec as
+ // late, ensure we're sufficiently larger than that
+ int t = 4 * config->cluster_hb_interval + 11;
+
+ if (config->cluster_hb_timeout < t)
+ {
+ LOG(0, 0, 0, "Heartbeat timeout %d too low, adjusting to %d\n", config->cluster_hb_timeout, t);
+ config->cluster_hb_timeout = t;
+ }
+
+ // Push timing changes to the slaves immediately if we're the master
+ if (config->cluster_iam_master)
+ cluster_heartbeat();
+
+ interval = config->cluster_hb_interval;
+ timeout = config->cluster_hb_timeout;
+ }
+
+ // Write PID file
+ if (*config->pid_file == '/' && !config->wrote_pid)
+ {
+ FILE *f;
+ if ((f = fopen(config->pid_file, "w")))
+ {
+ fprintf(f, "%d\n", getpid());
+ fclose(f);
+ config->wrote_pid = 1;
+ }
+ else
+ {
+ LOG(0, 0, 0, "Can't write to PID file %s: %s\n", config->pid_file, strerror(errno));
+ }
+ }
+}
+
+static void read_config_file()
+{
+ FILE *f;
+
+ if (!config->config_file) return;
+ if (!(f = fopen(config->config_file, "r")))