* Fri Mar 5 2004 David Parrish <david@dparrish.com> 1.1.0
authorfred_nerk <fred_nerk>
Fri, 5 Mar 2004 00:09:03 +0000 (00:09 +0000)
committerfred_nerk <fred_nerk>
Fri, 5 Mar 2004 00:09:03 +0000 (00:09 +0000)
- Change all strcpy() calls to strncpy() to avoid buffer overflow potential
- Add ICMP host unreachable support
- Logging to syslog if log_file = "syslog:facility"
- Now requires libcli 1.5
- All configuration moves to a config structure
- Ability to modify and write config on the fly through command-line interface
- Config file support is removed, and now handled by the cli
- Show hostname in cli prompt
- Keep current state type for tunnels
- Add uptime command do CLI, which also shows real-time bandwidth utilisation
- Add goodbye command to cluster master, which forces droppping a slave
- Cache IP address allocation, so that reconnecting users get the same address
- Fix tunnel resend timeouts, so that dead tunnels will be cleaned up
- Allocate tunnels and radius without using a linked list which had issues
- Fix some off-by-one errors in tunnel and session and radius arrays
- Save and reload ip address pool when dieing
- Check version and size of reloaded data when restarting
- Remove plugin_config support
- Remove old support for TBF which didn't work anyway. HTB is required to do throttling now.
- Add COPYING and Changes files

22 files changed:
Changes [new file with mode: 0644]
INSTALL
Makefile.in
arp.c
bounce.c [deleted file]
cli.c
cluster.c
cluster.h
cluster_master.c
cluster_slave.c
garden.c
icmp.c [new file with mode: 0644]
l2tpns.c
l2tpns.h
ll.c
md5.h
nsctl.c
plugin.h
ppp.c
radius.c
rl.c
throttle.c

diff --git a/Changes b/Changes
new file mode 100644 (file)
index 0000000..e2731d8
--- /dev/null
+++ b/Changes
@@ -0,0 +1,22 @@
+* Fri Mar 5 2004 David Parrish <david@dparrish.com> 1.1.0
+- Change all strcpy() calls to strncpy() to avoid buffer overflow potential
+- Add ICMP host unreachable support
+- Logging to syslog if log_file = "syslog:facility"
+- Now requires libcli 1.5
+- All configuration moves to a config structure
+- Ability to modify and write config on the fly through command-line interface
+- Config file support is removed, and now handled by the cli
+- Show hostname in cli prompt
+- Keep current state type for tunnels
+- Add uptime command do CLI, which also shows real-time bandwidth utilisation
+- Add goodbye command to cluster master, which forces droppping a slave
+- Cache IP address allocation, so that reconnecting users get the same address
+- Fix tunnel resend timeouts, so that dead tunnels will be cleaned up
+- Allocate tunnels and radius without using a linked list which had issues
+- Fix some off-by-one errors in tunnel and session and radius arrays
+- Save and reload ip address pool when dieing
+- Check version and size of reloaded data when restarting
+- Remove plugin_config support
+- Remove old support for TBF which didn't work anyway. HTB is required to do throttling now.
+- Add COPYING and Changes files
+
diff --git a/INSTALL b/INSTALL
index 4ccf61f..22f03a9 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -2,9 +2,8 @@ Brief Installation guide for L2TPNS
 
 1. Requirements
 
-    * You must have libcli installed to enable the command-line
-      interface. You can get it from http://sourceforge.net/projects/libcli.
-      If you don't have it, command-line support will not be compiled in.
+    * libcli 1.5.0 or greater
+      You can get it from http://sourceforge.net/projects/libcli.
 
     * A kernel with iptables support
 
index 404a715..7055f58 100644 (file)
@@ -16,6 +16,7 @@ INSTALL = @INSTALL@
 DEFS = @DEFS@
 
 OBJS=  md5.o \
+       icmp.o \
        cli.o \
        l2tpns.o \
        ppp.o \
diff --git a/arp.c b/arp.c
index 122880a..05929cd 100644 (file)
--- a/arp.c
+++ b/arp.c
@@ -42,15 +42,16 @@ void sendarp(int ifr_idx, const unsigned char* mac, ipt ip)
        memcpy(&buf.ar_sip, &ip, sizeof(ip));
        memcpy(buf.ar_tha, mac, ETH_ALEN);
        memcpy(&buf.ar_tip, &ip, sizeof(ip));
-       
+
        /* Now actually send the thing */
        fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_RARP));
 
        memset(&sll, 0, sizeof(sll));
        sll.sll_family = AF_PACKET;
-       strncpy(sll.sll_addr, mac, sizeof(sll.sll_addr)); /* Null-pad */
+       strncpy(sll.sll_addr, mac, sizeof(sll.sll_addr) - 1);
        sll.sll_halen = ETH_ALEN;
        sll.sll_ifindex = ifr_idx;
 
        sendto(fd, &buf, sizeof(buf), 0, (struct sockaddr*)&sll, sizeof(sll));
+       close(fd);
 }
diff --git a/bounce.c b/bounce.c
deleted file mode 100644 (file)
index 9d3f334..0000000
--- a/bounce.c
+++ /dev/null
@@ -1,90 +0,0 @@
-#include <arpa/inet.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <linux/if.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <linux/ip.h>
-#include <linux/udp.h>
-#include <pthread.h>
-#include <unistd.h>
-#include <signal.h>
-#include <getopt.h>
-
-#define PORT   39000
-
-void sigalarm(int junk);
-unsigned long long recv_count = 0;
-unsigned long pps = 0;
-unsigned long bytes = 0;
-unsigned port = PORT;
-
-int main(int argc, char *argv[])
-{
-       int on = 1;
-       struct sockaddr_in addr;
-       int s;
-       char *packet;
-
-       while ((s = getopt(argc, argv, "?p:")) > 0)
-       {
-               switch (s)
-               {
-                       case 'p' :
-                               port = atoi(optarg);
-                               break;
-                       case '?' :
-                               printf("Options:\n");
-                               printf("\t-p port to listen on\n");
-                               return(0);
-                               break;
-               }
-       }
-
-       memset(&addr, 0, sizeof(addr));
-       addr.sin_family = AF_INET;
-       addr.sin_port = htons(port);
-
-       s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
-       setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-       if (bind(s, (void *) &addr, sizeof(addr)) < 0)
-       {
-               perror("bind");
-               return -1;
-       }
-
-       signal(SIGALRM, sigalarm);
-       alarm(1);
-
-       printf("Waiting on port %d\n", port);
-       packet = (char *)malloc(65535);
-       while (1)
-       {
-               struct sockaddr_in addr;
-               int alen = sizeof(addr), l;
-
-               l = recvfrom(s, packet, 65535, 0, (void *) &addr, &alen);
-               if (l < 0) continue;
-               recv_count++;
-               pps++;
-               bytes += l;
-
-               sendto(s, packet, l, 0, (struct sockaddr *)&addr, alen);
-       }
-
-       free(packet);
-}
-
-void sigalarm(int junk)
-{
-       printf("Recv: %10llu %0.1fMbits/s (%lu pps)\n", recv_count, (bytes / 1024.0 / 1024.0 * 8), pps);
-       pps = bytes = 0;
-       alarm(1);
-}
-
diff --git a/cli.c b/cli.c
index e2ef57a..9957690 100644 (file)
--- a/cli.c
+++ b/cli.c
@@ -1,10 +1,11 @@
 // L2TPNS Command Line Interface
-// $Id: cli.c,v 1.1 2003/12/16 07:07:39 fred_nerk Exp $
+// $Id: cli.c,v 1.2 2004/03/05 00:09:03 fred_nerk Exp $
 // vim: sw=4 ts=8
 
 #include <stdio.h>
 #include <sys/file.h>
 #include <sys/stat.h>
+#include <syslog.h>
 #include <malloc.h>
 #include <string.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <signal.h>
 #include <unistd.h>
-#include <libcli.h>
 #include "l2tpns.h"
+#include "libcli.h"
 #include "util.h"
-#include "config.h"
-
-#ifdef HAVE_LIBCLI
 
 extern tunnelt *tunnel;
 extern sessiont *session;
@@ -34,11 +32,14 @@ extern int clifd, udpfd, tapfd, snoopfd, radfd, ifrfd, cluster_sockfd;
 extern sessionidt *cli_session_kill;
 extern tunnelidt *cli_tunnel_kill;
 extern tbft *filter_buckets;
+extern struct configt *config;
+extern struct config_descriptt config_values[];
+extern char hostname[];
 #ifdef RINGBUFFER
 extern struct Tringbuffer *ringbuffer;
 #endif
 
-char *rcs_id = "$Id: cli.c,v 1.1 2003/12/16 07:07:39 fred_nerk Exp $";
+char *rcs_id = "$Id: cli.c,v 1.2 2004/03/05 00:09:03 fred_nerk Exp $";
 
 char *debug_levels[] = {
     "CRIT",
@@ -62,27 +63,36 @@ struct
 int debug_session;
 int debug_tunnel;
 int debug_rb_tail;
-
-int cmd_show_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_show_tunnels(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_show_users(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_show_counters(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_show_version(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_show_pool(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_show_banana(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_clear_counters(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_drop_user(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_drop_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_drop_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_no_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_no_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_no_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_watch_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int cmd_watch_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, int argc);
-int regular_stuff(struct cli_def *cli, FILE *w);
+FILE *save_config_fh;
+
+int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_show_tunnels(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_show_users(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_show_radius(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_show_counters(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_show_version(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_show_pool(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_show_run(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_show_banana(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_show_plugins(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_write_memory(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_clear_counters(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_drop_user(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_drop_tunnel(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_drop_session(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_snoop(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_no_snoop(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_throttle(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_no_throttle(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_debug(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_no_debug(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_watch_session(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_watch_tunnel(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_set(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_load_plugin(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_remove_plugin(struct cli_def *cli, char *command, char **argv, int argc);
+int cmd_uptime(struct cli_def *cli, char *command, char **argv, int argc);
+int regular_stuff(struct cli_def *cli);
 
 void init_cli()
 {
@@ -95,38 +105,57 @@ void init_cli()
     cli = cli_init();
 
     c = cli_register_command(cli, NULL, "show", NULL, NULL);
-    cli_register_command(cli, c, "session", cmd_show_session, "Show a list of sessions");
-    cli_register_command(cli, c, "tunnels", cmd_show_tunnels, NULL);
-    cli_register_command(cli, c, "users", cmd_show_users, NULL);
-    cli_register_command(cli, c, "version", cmd_show_version, NULL);
+    cli_register_command(cli, c, "session", cmd_show_session, "Show a list of sessions or details for a single session");
+    cli_register_command(cli, c, "tunnels", cmd_show_tunnels, "Show a list of tunnels or details for a single tunnel");
+    cli_register_command(cli, c, "users", cmd_show_users, "Show a list of all connected users");
+    cli_register_command(cli, c, "version", cmd_show_version, "Show currently running software version");
     cli_register_command(cli, c, "banana", cmd_show_banana, "Show a banana");
-    cli_register_command(cli, c, "pool", cmd_show_pool, NULL);
+    cli_register_command(cli, c, "pool", cmd_show_pool, "Show the IP address allocation pool");
+    cli_register_command(cli, c, "running-config", cmd_show_run, "Show the currently running configuration");
+    cli_register_command(cli, c, "radius", cmd_show_radius, "Show active radius queries");
+    cli_register_command(cli, c, "plugins", cmd_show_plugins, "List all installed plugins");
 
 #ifdef STATISTICS
-    cli_register_command(cli, c, "counters", cmd_show_counters, NULL);
+    cli_register_command(cli, c, "counters", cmd_show_counters, "Display all the internal counters and running totals");
 
     c = cli_register_command(cli, NULL, "clear", NULL, NULL);
-    cli_register_command(cli, c, "counters", cmd_clear_counters, NULL);
+    cli_register_command(cli, c, "counters", cmd_clear_counters, "Clear internal counters");
 #endif
 
-    cli_register_command(cli, NULL, "snoop", cmd_snoop, NULL);
-    cli_register_command(cli, NULL, "throttle", cmd_throttle, NULL);
+    cli_register_command(cli, NULL, "uptime", cmd_uptime, "Show uptime and bandwidth utilisation");
+
+    c = cli_register_command(cli, NULL, "write", NULL, NULL);
+    cli_register_command(cli, c, "memory", cmd_write_memory, "Save the running config to flash");
+    cli_register_command(cli, c, "terminal", cmd_show_run, "Show the running config");
+
+    cli_register_command(cli, NULL, "snoop", cmd_snoop, "Temporarily enable interception for a user");
+    cli_register_command(cli, NULL, "throttle", cmd_throttle, "Temporarily enable throttling for a user");
 
     c = cli_register_command(cli, NULL, "no", NULL, NULL);
-    cli_register_command(cli, c, "snoop", cmd_no_snoop, NULL);
-    cli_register_command(cli, c, "throttle", cmd_no_throttle, NULL);
-    cli_register_command(cli, c, "debug", cmd_no_debug, NULL);
+    cli_register_command(cli, c, "snoop", cmd_no_snoop, "Temporarily disable interception for a user");
+    cli_register_command(cli, c, "throttle", cmd_no_throttle, "Temporarily disable throttling for a user");
+    cli_register_command(cli, c, "debug", cmd_no_debug, "Turn off logging of a certain level of debugging");
 
     c = cli_register_command(cli, NULL, "drop", NULL, NULL);
-    cli_register_command(cli, c, "user", cmd_drop_user, NULL);
-    cli_register_command(cli, c, "tunnel", cmd_drop_tunnel, NULL);
-    cli_register_command(cli, c, "session", cmd_drop_session, NULL);
+    cli_register_command(cli, c, "user", cmd_drop_user, "Disconnect a user");
+    cli_register_command(cli, c, "tunnel", cmd_drop_tunnel, "Disconnect a tunnel and all sessions on that tunnel");
+    cli_register_command(cli, c, "session", cmd_drop_session, "Disconnect a session");
 
-    cli_register_command(cli, NULL, "debug", cmd_debug, "Specify a debugging level");
+    cli_register_command(cli, NULL, "debug", cmd_debug, "Set the level of logging that is shown on the console");
 
+    /*
     c = cli_register_command(cli, NULL, "watch", NULL, NULL);
-    cli_register_command(cli, c, "session", cmd_watch_session, "Dump logs for a tunnel");
+    cli_register_command(cli, c, "session", cmd_watch_session, "Dump logs for a session");
     cli_register_command(cli, c, "tunnel", cmd_watch_tunnel, "Dump logs for a tunnel");
+    */
+
+    c = cli_register_command(cli, NULL, "load", NULL, NULL);
+    cli_register_command(cli, c, "plugin", cmd_load_plugin, "Load a plugin");
+
+    c = cli_register_command(cli, NULL, "remove", NULL, NULL);
+    cli_register_command(cli, c, "plugin", cmd_remove_plugin, "Remove a plugin");
+
+    cli_register_command(cli, NULL, "set", cmd_set, "Set a configuration variable");
 
     // Enable regular processing
     cli_regular(cli, regular_stuff);
@@ -173,7 +202,7 @@ void init_cli()
 
 void cli_do(int sockfd)
 {
-    if ((cli_pid = fork())) return;
+    if (fork()) return;
 
     // Close sockets
     if (udpfd) close(udpfd); udpfd = 0;
@@ -202,14 +231,31 @@ void cli_do(int sockfd)
     memset(&debug_flags, 0, sizeof(debug_flags));
     debug_flags.critical = 1;
 
-    cli_loop(cli, sockfd, "l2tpns> ");
+    {
+       char prompt[1005];
+       snprintf(prompt, 1005, "%s> ", hostname);
+       cli_loop(cli, sockfd, prompt);
+    }
 
     close(sockfd);
     log(3, 0, 0, 0, "Closed CLI connection\n");
     exit(0);
 }
 
-int cmd_show_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+void cli_print_log(struct cli_def *cli, char *string)
+{
+    log(3, 0, 0, 0, "%s\n", string);
+}
+
+void cli_do_file(FILE *fh)
+{
+    log(3, 0, 0, 0, "Reading configuration file\n");
+    cli_print_callback(cli, cli_print_log);
+    cli_file(cli, fh);
+    cli_print_callback(cli, NULL);
+}
+
+int cmd_show_session(struct cli_def *cli, char *command, char **argv, int argc)
 {
     int i;
     time_t time_now;
@@ -224,34 +270,34 @@ int cmd_show_session(struct cli_def *cli, FILE *w, char *command, char **argv, i
            s = atoi(argv[i]);
            if (!s || s > MAXSESSION)
            {
-               fprintf(w, "Invalid session id \"%s\"\r\n", argv[i]);
+               cli_print(cli, "Invalid session id \"%s\"", argv[i]);
                continue;
            }
-           fprintf(w, "\r\nSession %d:\r\n", s);
-           fprintf(w, "        User:           %s\r\n", session[s].user[0] ? session[s].user : "none");
-           fprintf(w, "        Calling Num:    %s\r\n", session[s].calling);
-           fprintf(w, "        Called Num:     %s\r\n", session[s].called);
-           fprintf(w, "        Tunnel ID:      %d\r\n", session[s].tunnel);
-           fprintf(w, "        IP address:     %s\r\n", inet_toa(htonl(session[s].ip)));
-           fprintf(w, "        HSD sid:        %lu\r\n", session[s].sid);
-           fprintf(w, "        Idle time:      %u seconds\r\n", abs(time_now - session[s].last_packet));
-           fprintf(w, "        Next Recv:      %u\r\n", session[s].nr);
-           fprintf(w, "        Next Send:      %u\r\n", session[s].ns);
-           fprintf(w, "        Bytes In/Out:   %lu/%lu\r\n", (unsigned long)session[s].cin, (unsigned long)session[s].cout);
-           fprintf(w, "        Pkts In/Out:    %lu/%lu\r\n", (unsigned long)session[s].pin, (unsigned long)session[s].pout);
-           fprintf(w, "        Radius Session: %u\r\n", session[s].radius);
-           fprintf(w, "        Rx Speed:       %lu\r\n", session[s].rx_connect_speed);
-           fprintf(w, "        Tx Speed:       %lu\r\n", session[s].tx_connect_speed);
-           fprintf(w, "        Intercepted:    %s\r\n", session[s].snoop ? "YES" : "no");
-           fprintf(w, "        Throttled:      %s\r\n", session[s].throttle ? "YES" : "no");
-           fprintf(w, "        Walled Garden:  %s\r\n", session[s].walled_garden ? "YES" : "no");
-           fprintf(w, "        Filter Bucket:  %s\r\n", session[s].tbf ? filter_buckets[session[s].tbf].handle : "none");
+           cli_print(cli, "\r\nSession %d:", s);
+           cli_print(cli, "    User:           %s", session[s].user[0] ? session[s].user : "none");
+           cli_print(cli, "    Calling Num:    %s", session[s].calling);
+           cli_print(cli, "    Called Num:     %s", session[s].called);
+           cli_print(cli, "    Tunnel ID:      %d", session[s].tunnel);
+           cli_print(cli, "    IP address:     %s", inet_toa(htonl(session[s].ip)));
+           cli_print(cli, "    HSD sid:        %lu", session[s].sid);
+           cli_print(cli, "    Idle time:      %u seconds", abs(time_now - session[s].last_packet));
+           cli_print(cli, "    Next Recv:      %u", session[s].nr);
+           cli_print(cli, "    Next Send:      %u", session[s].ns);
+           cli_print(cli, "    Bytes In/Out:   %lu/%lu", (unsigned long)session[s].cin, (unsigned long)session[s].total_cout);
+           cli_print(cli, "    Pkts In/Out:    %lu/%lu", (unsigned long)session[s].pin, (unsigned long)session[s].pout);
+           cli_print(cli, "    Radius Session: %u", session[s].radius);
+           cli_print(cli, "    Rx Speed:       %lu", session[s].rx_connect_speed);
+           cli_print(cli, "    Tx Speed:       %lu", session[s].tx_connect_speed);
+           cli_print(cli, "    Intercepted:    %s", session[s].snoop ? "YES" : "no");
+           cli_print(cli, "    Throttled:      %s", session[s].throttle ? "YES" : "no");
+           cli_print(cli, "    Servicenet:     %s", session[s].servicenet ? "YES" : "no");
+           cli_print(cli, "    Filter Bucket:  %s", session[s].tbf ? filter_buckets[session[s].tbf].handle : "none");
        }
        return CLI_OK;
     }
 
     // Show Summary
-    fprintf(w, "  %s %4s %-32s %-15s %s %s %s %10s %10s %10s %4s %-15s %s\r\n",
+    cli_print(cli, "  %s %4s %-32s %-15s %s %s %s %10s %10s %10s %4s %-15s %s",
            "SID",
            "TID",
            "Username",
@@ -265,23 +311,23 @@ int cmd_show_session(struct cli_def *cli, FILE *w, char *command, char **argv, i
            "idle",
            "LAC",
            "CLI");
-    for (i = 0; i < MAXSESSION; i++)
+    for (i = 1; i < MAXSESSION; i++)
     {
        char *userip, *tunnelip;
        if (!session[i].opened) continue;
        userip = strdup(inet_toa(htonl(session[i].ip)));
        tunnelip = strdup(inet_toa(htonl(tunnel[ session[i].tunnel ].ip)));
-       fprintf(w, "%5d %4d %-32s %-15s %s %s %s %10lu %10lu %10lu %4u %-15s %s\r\n",
+       cli_print(cli, "%5d %4d %-32s %-15s %s %s %s %10u %10lu %10lu %4u %-15s %s",
                i,
                session[i].tunnel,
                session[i].user[0] ? session[i].user : "*",
                userip,
                (session[i].snoop) ? "Y" : "N",
                (session[i].throttle) ? "Y" : "N",
-               (session[i].walled_garden) ? "Y" : "N",
-               (unsigned long)session[i].opened,
-               (unsigned long)session[i].cout,
-               (unsigned long)session[i].cin,
+               (session[i].servicenet) ? "Y" : "N",
+               abs(time_now - (unsigned long)session[i].opened),
+               (unsigned long)session[i].total_cout,
+               (unsigned long)session[i].total_cin,
                abs(time_now - (session[i].last_packet ? session[i].last_packet : time_now)),
                 tunnelip,
                session[i].calling[0] ? session[i].calling : "*");
@@ -291,235 +337,381 @@ int cmd_show_session(struct cli_def *cli, FILE *w, char *command, char **argv, i
     return CLI_OK;
 }
 
-int cmd_show_tunnels(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+int cmd_show_tunnels(struct cli_def *cli, char *command, char **argv, int argc)
 {
-    int i, x;
+    int i, x, show_all = 0;
     time_t time_now;
+    char *states[] = {
+       "Free",
+       "Open",
+       "Closing",
+       "Opening",
+    };
 
     time(&time_now);
     if (argc > 0)
     {
-       // Show individual tunnel
-       for (i = 0; i < argc; i++)
+       if (strcmp(argv[0], "all") == 0)
        {
-           char s[65535] = {0};
-           unsigned int t;
-           t = atoi(argv[i]);
-           if (!t || t > MAXTUNNEL)
+           show_all = 1;
+       }
+       else
+       {
+           // Show individual tunnel
+           for (i = 0; i < argc; i++)
            {
-               fprintf(w, "Invalid tunnel id \"%s\"\r\n", argv[i]);
-               continue;
+               char s[65535] = {0};
+               unsigned int t;
+               t = atoi(argv[i]);
+               if (!t || t > MAXTUNNEL)
+               {
+                   cli_print(cli, "Invalid tunnel id \"%s\"", argv[i]);
+                   continue;
+               }
+               cli_print(cli, "\r\nTunnel %d:", t);
+               cli_print(cli, "        State:          %s", states[tunnel[t].state]);
+               cli_print(cli, "        Hostname:       %s", tunnel[t].hostname[0] ? tunnel[t].hostname : "(none)");
+               cli_print(cli, "        Remote IP:      %s", inet_toa(htonl(tunnel[t].ip)));
+               cli_print(cli, "        Remote Port:    %d", tunnel[t].port);
+               cli_print(cli, "        Rx Window:      %u", tunnel[t].window);
+               cli_print(cli, "        Next Recv:      %u", tunnel[t].nr);
+               cli_print(cli, "        Next Send:      %u", tunnel[t].ns);
+               cli_print(cli, "        Queue Len:      %u", tunnel[t].controlc);
+               cli_print(cli, "        Last Packet Age:%u", (unsigned)(time_now - tunnel[t].last));
+
+               for (x = 0; x < MAXSESSION; x++)
+                       if (session[x].tunnel == t && session[x].opened && !session[x].die)
+                               sprintf(s, "%s%u ", s, x);
+               cli_print(cli, "        Sessions:       %s", s);
            }
-           fprintf(w, "\r\nTunnel %d:\r\n", t);
-           fprintf(w, "        Hostname:       %s\r\n", tunnel[t].hostname[0] ? tunnel[t].hostname : "(none)");
-           fprintf(w, "        Remote IP:      %s\r\n", inet_toa(htonl(tunnel[t].ip)));
-           fprintf(w, "        Remote Port:    %d\r\n", tunnel[t].port);
-           fprintf(w, "        Rx Window:      %u\r\n", tunnel[t].window);
-           fprintf(w, "        Next Recv:      %u\r\n", tunnel[t].nr);
-           fprintf(w, "        Next Send:      %u\r\n", tunnel[t].ns);
-           fprintf(w, "        Queue Len:      %u\r\n", tunnel[t].controlc);
-           fprintf(w, "        Last Packet Age:%u\r\n", (unsigned)(time_now - tunnel[t].last));
-
-           for (x = 0; x < MAXSESSION; x++)
-                   if (session[x].tunnel == t && session[x].opened && !session[x].die)
-                           sprintf(s, "%s%u ", s, x);
-           fprintf(w, "        Sessions:       %s\r\n", s);
+           return CLI_OK;
        }
-       return CLI_OK;
     }
 
     // Show tunnel summary
-    fprintf(w, "%s %s %s %s\r\n",
+    cli_print(cli, "%s %s %s %s %s",
            "TID",
            "Hostname",
            "IP",
+           "State",
            "Sessions");
-    for (i = 0; i < MAXTUNNEL; i++)
+    for (i = 1; i < MAXTUNNEL; i++)
     {
        int sessions = 0;
-       if (!tunnel[i].ip || tunnel[i].die || !tunnel[i].hostname[0]) continue;
+       if (!show_all && (!tunnel[i].ip || tunnel[i].die || !tunnel[i].hostname[0])) continue;
 
        for (x = 0; x < MAXSESSION; x++) if (session[x].tunnel == i && session[x].opened && !session[x].die) sessions++;
-       fprintf(w, "%d %s %s %d\r\n",
+       cli_print(cli, "%d %s %s %s %d",
                i,
-               tunnel[i].hostname,
+               *tunnel[i].hostname ? tunnel[i].hostname : "(null)",
                inet_toa(htonl(tunnel[i].ip)),
+               states[tunnel[i].state],
                sessions);
     }
     return CLI_OK;
 }
 
-int cmd_show_users(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+int cmd_show_users(struct cli_def *cli, char *command, char **argv, int argc)
 {
     int i;
     for (i = 0; i < MAXSESSION; i++)
     {
        if (!session[i].opened) continue;
        if (!session[i].user[0]) continue;
-       fprintf(w, "%s\r\n",
+       cli_print(cli, "%s",
                session[i].user);
     }
     return CLI_OK;
 }
 
-int cmd_show_counters(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+int cmd_show_counters(struct cli_def *cli, char *command, char **argv, int argc)
 {
-    fprintf(w, "%-10s %-8s %-10s %-8s\r\n", "Ethernet", "Bytes", "Packets", "Errors");
-    fprintf(w, "%-10s %8lu %8lu %8lu\r\n", "RX",
+    cli_print(cli, "%-10s %-8s %-10s %-8s", "Ethernet", "Bytes", "Packets", "Errors");
+    cli_print(cli, "%-10s %8lu %8lu %8lu", "RX",
            GET_STAT(tap_rx_bytes),
            GET_STAT(tap_rx_packets),
            GET_STAT(tap_rx_errors));
-    fprintf(w, "%-10s %8lu %8lu %8lu\r\n", "TX",
+    cli_print(cli, "%-10s %8lu %8lu %8lu", "TX",
            GET_STAT(tap_tx_bytes),
            GET_STAT(tap_tx_packets),
            GET_STAT(tap_tx_errors));
-    fprintf(w, "\r\n");
+    cli_print(cli, "");
 
-    fprintf(w, "%-10s %-8s %-10s %-8s %-8s\r\n", "Tunnel", "Bytes", "Packets", "Errors", "Retries");
-    fprintf(w, "%-10s %8lu %8lu %8lu %8lu\r\n", "RX",
+    cli_print(cli, "%-10s %-8s %-10s %-8s %-8s", "Tunnel", "Bytes", "Packets", "Errors", "Retries");
+    cli_print(cli, "%-10s %8lu %8lu %8lu %8lu", "RX",
            GET_STAT(tunnel_rx_bytes),
            GET_STAT(tunnel_rx_packets),
            GET_STAT(tunnel_rx_errors),
            0L);
-    fprintf(w, "%-10s %8lu %8lu %8lu %8lu\r\n", "TX",
+    cli_print(cli, "%-10s %8lu %8lu %8lu %8lu", "TX",
            GET_STAT(tunnel_tx_bytes),
            GET_STAT(tunnel_tx_packets),
            GET_STAT(tunnel_rx_errors),
            GET_STAT(tunnel_retries));
-    fprintf(w, "\r\n");
-
-    fprintf(w, "%-30s%-10s\r\n", "Counter", "Value");
-    fprintf(w, "-----------------------------------------\r\n");
-    fprintf(w, "%-30s%lu\r\n", "radius_retries",       GET_STAT(radius_retries));
-    fprintf(w, "%-30s%lu\r\n", "arp_errors",           GET_STAT(arp_errors));
-    fprintf(w, "%-30s%lu\r\n", "arp_replies",          GET_STAT(arp_replies));
-    fprintf(w, "%-30s%lu\r\n", "arp_discarded",                GET_STAT(arp_discarded));
-    fprintf(w, "%-30s%lu\r\n", "arp_sent",             GET_STAT(arp_sent));
-    fprintf(w, "%-30s%lu\r\n", "arp_recv",             GET_STAT(arp_recv));
-    fprintf(w, "%-30s%lu\r\n", "packets_snooped",      GET_STAT(packets_snooped));
-    fprintf(w, "%-30s%lu\r\n", "tunnel_created",       GET_STAT(tunnel_created));
-    fprintf(w, "%-30s%lu\r\n", "session_created",      GET_STAT(session_created));
-    fprintf(w, "%-30s%lu\r\n", "tunnel_timeout",       GET_STAT(tunnel_timeout));
-    fprintf(w, "%-30s%lu\r\n", "session_timeout",      GET_STAT(session_timeout));
-    fprintf(w, "%-30s%lu\r\n", "radius_timeout",       GET_STAT(radius_timeout));
-    fprintf(w, "%-30s%lu\r\n", "radius_overflow",      GET_STAT(radius_overflow));
-    fprintf(w, "%-30s%lu\r\n", "tunnel_overflow",      GET_STAT(tunnel_overflow));
-    fprintf(w, "%-30s%lu\r\n", "session_overflow",     GET_STAT(session_overflow));
-    fprintf(w, "%-30s%lu\r\n", "ip_allocated",         GET_STAT(ip_allocated));
-    fprintf(w, "%-30s%lu\r\n", "ip_freed",             GET_STAT(ip_freed));
+    cli_print(cli, "");
+
+    cli_print(cli, "%-30s%-10s", "Counter", "Value");
+    cli_print(cli, "-----------------------------------------");
+    cli_print(cli, "%-30s%lu", "radius_retries",       GET_STAT(radius_retries));
+    cli_print(cli, "%-30s%lu", "arp_errors",           GET_STAT(arp_errors));
+    cli_print(cli, "%-30s%lu", "arp_replies",          GET_STAT(arp_replies));
+    cli_print(cli, "%-30s%lu", "arp_discarded",                GET_STAT(arp_discarded));
+    cli_print(cli, "%-30s%lu", "arp_sent",             GET_STAT(arp_sent));
+    cli_print(cli, "%-30s%lu", "arp_recv",             GET_STAT(arp_recv));
+    cli_print(cli, "%-30s%lu", "packets_snooped",      GET_STAT(packets_snooped));
+    cli_print(cli, "%-30s%lu", "tunnel_created",       GET_STAT(tunnel_created));
+    cli_print(cli, "%-30s%lu", "session_created",      GET_STAT(session_created));
+    cli_print(cli, "%-30s%lu", "tunnel_timeout",       GET_STAT(tunnel_timeout));
+    cli_print(cli, "%-30s%lu", "session_timeout",      GET_STAT(session_timeout));
+    cli_print(cli, "%-30s%lu", "radius_timeout",       GET_STAT(radius_timeout));
+    cli_print(cli, "%-30s%lu", "radius_overflow",      GET_STAT(radius_overflow));
+    cli_print(cli, "%-30s%lu", "tunnel_overflow",      GET_STAT(tunnel_overflow));
+    cli_print(cli, "%-30s%lu", "session_overflow",     GET_STAT(session_overflow));
+    cli_print(cli, "%-30s%lu", "ip_allocated",         GET_STAT(ip_allocated));
+    cli_print(cli, "%-30s%lu", "ip_freed",             GET_STAT(ip_freed));
 
 #ifdef STAT_CALLS
-    fprintf(w, "\n%-30s%-10s\r\n", "Counter", "Value");
-    fprintf(w, "-----------------------------------------\r\n");
-    fprintf(w, "%-30s%lu\r\n", "call_processtap",      GET_STAT(call_processtap));
-    fprintf(w, "%-30s%lu\r\n", "call_processarp",      GET_STAT(call_processarp));
-    fprintf(w, "%-30s%lu\r\n", "call_processipout",    GET_STAT(call_processipout));
-    fprintf(w, "%-30s%lu\r\n", "call_processudp",      GET_STAT(call_processudp));
-    fprintf(w, "%-30s%lu\r\n", "call_processpap",      GET_STAT(call_processpap));
-    fprintf(w, "%-30s%lu\r\n", "call_processchap",     GET_STAT(call_processchap));
-    fprintf(w, "%-30s%lu\r\n", "call_processlcp",      GET_STAT(call_processlcp));
-    fprintf(w, "%-30s%lu\r\n", "call_processipcp",     GET_STAT(call_processipcp));
-    fprintf(w, "%-30s%lu\r\n", "call_processipin",     GET_STAT(call_processipin));
-    fprintf(w, "%-30s%lu\r\n", "call_processccp",      GET_STAT(call_processccp));
-    fprintf(w, "%-30s%lu\r\n", "call_processrad",      GET_STAT(call_processrad));
-    fprintf(w, "%-30s%lu\r\n", "call_sendarp",         GET_STAT(call_sendarp));
-    fprintf(w, "%-30s%lu\r\n", "call_sendipcp",                GET_STAT(call_sendipcp));
-    fprintf(w, "%-30s%lu\r\n", "call_sendchap",                GET_STAT(call_sendchap));
-    fprintf(w, "%-30s%lu\r\n", "call_sessionbyip",     GET_STAT(call_sessionbyip));
-    fprintf(w, "%-30s%lu\r\n", "call_sessionbyuser",   GET_STAT(call_sessionbyuser));
-    fprintf(w, "%-30s%lu\r\n", "call_tunnelsend",      GET_STAT(call_tunnelsend));
-    fprintf(w, "%-30s%lu\r\n", "call_tunnelkill",      GET_STAT(call_tunnelkill));
-    fprintf(w, "%-30s%lu\r\n", "call_tunnelshutdown",  GET_STAT(call_tunnelshutdown));
-    fprintf(w, "%-30s%lu\r\n", "call_sessionkill",     GET_STAT(call_sessionkill));
-    fprintf(w, "%-30s%lu\r\n", "call_sessionshutdown", GET_STAT(call_sessionshutdown));
-    fprintf(w, "%-30s%lu\r\n", "call_sessionsetup",    GET_STAT(call_sessionsetup));
-    fprintf(w, "%-30s%lu\r\n", "call_assign_ip_address",GET_STAT(call_assign_ip_address));
-    fprintf(w, "%-30s%lu\r\n", "call_free_ip_address", GET_STAT(call_free_ip_address));
-    fprintf(w, "%-30s%lu\r\n", "call_dump_acct_info",  GET_STAT(call_dump_acct_info));
-    fprintf(w, "%-30s%lu\r\n", "call_radiussend",      GET_STAT(call_radiussend));
-    fprintf(w, "%-30s%lu\r\n", "call_radiusretry",     GET_STAT(call_radiusretry));
+    cli_print(cli, "\n%-30s%-10s", "Counter", "Value");
+    cli_print(cli, "-----------------------------------------");
+    cli_print(cli, "%-30s%lu", "call_processtap",      GET_STAT(call_processtap));
+    cli_print(cli, "%-30s%lu", "call_processarp",      GET_STAT(call_processarp));
+    cli_print(cli, "%-30s%lu", "call_processipout",    GET_STAT(call_processipout));
+    cli_print(cli, "%-30s%lu", "call_processudp",      GET_STAT(call_processudp));
+    cli_print(cli, "%-30s%lu", "call_processpap",      GET_STAT(call_processpap));
+    cli_print(cli, "%-30s%lu", "call_processchap",     GET_STAT(call_processchap));
+    cli_print(cli, "%-30s%lu", "call_processlcp",      GET_STAT(call_processlcp));
+    cli_print(cli, "%-30s%lu", "call_processipcp",     GET_STAT(call_processipcp));
+    cli_print(cli, "%-30s%lu", "call_processipin",     GET_STAT(call_processipin));
+    cli_print(cli, "%-30s%lu", "call_processccp",      GET_STAT(call_processccp));
+    cli_print(cli, "%-30s%lu", "call_processrad",      GET_STAT(call_processrad));
+    cli_print(cli, "%-30s%lu", "call_sendarp",         GET_STAT(call_sendarp));
+    cli_print(cli, "%-30s%lu", "call_sendipcp",                GET_STAT(call_sendipcp));
+    cli_print(cli, "%-30s%lu", "call_sendchap",                GET_STAT(call_sendchap));
+    cli_print(cli, "%-30s%lu", "call_sessionbyip",     GET_STAT(call_sessionbyip));
+    cli_print(cli, "%-30s%lu", "call_sessionbyuser",   GET_STAT(call_sessionbyuser));
+    cli_print(cli, "%-30s%lu", "call_tunnelsend",      GET_STAT(call_tunnelsend));
+    cli_print(cli, "%-30s%lu", "call_tunnelkill",      GET_STAT(call_tunnelkill));
+    cli_print(cli, "%-30s%lu", "call_tunnelshutdown",  GET_STAT(call_tunnelshutdown));
+    cli_print(cli, "%-30s%lu", "call_sessionkill",     GET_STAT(call_sessionkill));
+    cli_print(cli, "%-30s%lu", "call_sessionshutdown", GET_STAT(call_sessionshutdown));
+    cli_print(cli, "%-30s%lu", "call_sessionsetup",    GET_STAT(call_sessionsetup));
+    cli_print(cli, "%-30s%lu", "call_assign_ip_address",GET_STAT(call_assign_ip_address));
+    cli_print(cli, "%-30s%lu", "call_free_ip_address", GET_STAT(call_free_ip_address));
+    cli_print(cli, "%-30s%lu", "call_dump_acct_info",  GET_STAT(call_dump_acct_info));
+    cli_print(cli, "%-30s%lu", "call_radiussend",      GET_STAT(call_radiussend));
+    cli_print(cli, "%-30s%lu", "call_radiusretry",     GET_STAT(call_radiusretry));
 #endif
     return CLI_OK;
 }
 
-int cmd_show_version(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+int cmd_show_version(struct cli_def *cli, char *command, char **argv, int argc)
 {
-    fprintf(w, "L2TPNS %s\r\n", VERSION);
-    fprintf(w, "ID: %s\r\n", rcs_id);
+    cli_print(cli, "L2TPNS %s", VERSION);
+    cli_print(cli, "ID: %s", rcs_id);
     return CLI_OK;
 }
-int cmd_show_pool(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+
+int cmd_show_pool(struct cli_def *cli, char *command, char **argv, int argc)
 {
     int i;
-    int used = 0, free = 0;
+    int used = 0, free = 0, show_all = 0;
+    time_t time_now;
+
+    if (argc > 0 && strcmp(argv[0], "all") == 0)
+       show_all = 1;
 
-    fprintf(w, "%-15s  %4s   %8s %s\r\n", "IP Address", "Used", "Session", "User");
+    time(&time_now);
+    cli_print(cli, "%-15s %4s %8s %s", "IP Address", "Used", "Session", "User");
     for (i = 0; i < MAXIPPOOL; i++)
     {
-       sessionidt s = 0;
-
        if (!ip_address_pool[i].address) continue;
        if (ip_address_pool[i].assigned)
        {
+           sessionidt s = sessionbyip(ip_address_pool[i].address);
+           cli_print(cli, "%-15s    Y %8d %s",
+               inet_toa(ip_address_pool[i].address), s, session[s].user);
+
            used++;
-           s = sessionbyip(ip_address_pool[i].address);
        }
        else
        {
+           if (ip_address_pool[i].last)
+               cli_print(cli, "%-15s    N %8s [%s] %ds",
+                   inet_toa(ip_address_pool[i].address), "",
+                   ip_address_pool[i].user, time_now - ip_address_pool[i].last);
+           else if (show_all)
+               cli_print(cli, "%-15s    N", inet_toa(ip_address_pool[i].address));
+
            free++;
        }
+    }
+
+    if (!show_all)
+       cli_print(cli, "(Not displaying unused addresses)");
+
+    cli_print(cli, "\r\nFree: %d\r\nUsed: %d", free, used);
+    return CLI_OK;
+}
+
+void print_save_config(struct cli_def *cli, char *string)
+{
+    if (save_config_fh)
+       fprintf(save_config_fh, "%s\n", string);
+}
+
+int cmd_write_memory(struct cli_def *cli, char *command, char **argv, int argc)
+{
+    if ((save_config_fh = fopen(config->config_file, "w")))
+    {
+       cli_print(cli, "Writing configuration");
+       cli_print_callback(cli, print_save_config);
+       cmd_show_run(cli, command, argv, argc);
+       cli_print_callback(cli, NULL);
+       fclose(save_config_fh);
+       sleep(1);
+    }
+    else
+    {
+       cli_print(cli, "Error writing configuration: %s", strerror(errno));
+    }
+    return CLI_OK;
+}
+
+int cmd_show_run(struct cli_def *cli, char *command, char **argv, int argc)
+{
+    int i;
+
+    cli_print(cli, "# Current configuration:");
+
+    for (i = 0; config_values[i].key; i++)
+    {
+       void *value = ((void *)config) + config_values[i].offset;
+       if (config_values[i].type == STRING)
+           cli_print(cli, "set %s \"%.*s\"", config_values[i].key, config_values[i].size, (char *)value);
+       else if (config_values[i].type == IP)
+           cli_print(cli, "set %s %s", config_values[i].key, inet_toa(*(unsigned *)value));
+       else if (config_values[i].type == SHORT)
+           cli_print(cli, "set %s %hu", config_values[i].key, *(short *)value);
+       else if (config_values[i].type == BOOL)
+           cli_print(cli, "set %s %s", config_values[i].key, (*(int *)value) ? "yes" : "no");
+       else if (config_values[i].type == INT)
+           cli_print(cli, "set %s %d", config_values[i].key, *(int *)value);
+       else if (config_values[i].type == UNSIGNED_LONG)
+           cli_print(cli, "set %s %lu", config_values[i].key, *(unsigned long *)value);
+    }
+
+    cli_print(cli, "# Plugins");
+    for (i = 0; i < MAXPLUGINS; i++)
+    {
+       if (*config->plugins[i])
+       {
+           cli_print(cli, "load plugin \"%s\"", config->plugins[i]);
+       }
+    }
+
+    cli_print(cli, "# end");
+    return CLI_OK;
+}
 
-       fprintf(w, "%-15s %4s %8d %s\r\n",
-               inet_toa(ip_address_pool[i].address),
-               (s) ? "Y" : "N",
-               s,
-               session[s].user);
+int cmd_show_radius(struct cli_def *cli, char *command, char **argv, int argc)
+{
+    char *states[] = {
+       "NULL",
+       "CHAP",
+       "AUTH",
+       "IPCP",
+       "START",
+       "STOP",
+       "WAIT",
+    };
+    int i, free = 0, used = 0, show_all = 0;
+    time_t time_now;
+
+    cli_print(cli, "%6s%6s%9s%9s%4s", "Radius", "State", "Session", "Retry", "Try");
+
+    time(&time_now);
+
+    if (argc > 0 && strcmp(argv[0], "all") == 0)
+       show_all = 1;
+
+    for (i = 1; i < MAXRADIUS; i++)
+    {
+       if (radius[i].state == RADIUSNULL)
+           free++;
+       else
+           used++;
+
+       if (!show_all && radius[i].state == RADIUSNULL) continue;
+
+       cli_print(cli, "%6d%6s%9d%9u%4d",
+               i,
+               states[radius[i].state],
+               radius[i].session,
+               radius[i].retry,
+               radius[i].try);
     }
-    fprintf(w, "\r\nFree: %d\r\nUsed: %d\r\n", free, used);
+
+    cli_print(cli, "\r\nFree: %d\r\nUsed: %d", free, used);
+
     return CLI_OK;
 }
-int cmd_show_banana(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
-{
-    fputs(" _\r\n"
-          "//\\\r\n"
-          "V  \\\r\n"
-          " \\  \\_\r\n"
-          "  \\,'.`-.\r\n"
-          "   |\\ `. `.\r\n"
-          "   ( \\  `. `-.                        _,.-:\\\r\n"
-          "    \\ \\   `.  `-._             __..--' ,-';/\r\n"
-          "     \\ `.   `-.   `-..___..---'   _.--' ,'/\r\n"
-          "      `. `.    `-._        __..--'    ,' /\r\n"
-          "        `. `-_     ``--..''       _.-' ,'\r\n"
-          "          `-_ `-.___        __,--'   ,'\r\n"
-          "             `-.__  `----\"\"\"    __.-'\r\n"
-          "hh                `--..____..--'\r\n", w);
 
+int cmd_show_plugins(struct cli_def *cli, char *command, char **argv, int argc)
+{
+    int i;
+    cli_print(cli, "Plugins currently loaded:");
+    for (i = 0; i < MAXPLUGINS; i++)
+    {
+       if (*config->plugins[i])
+       {
+           cli_print(cli, "  %s", config->plugins[i]);
+       }
+    }
     return CLI_OK;
 }
 
-int cmd_clear_counters(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+int cmd_show_banana(struct cli_def *cli, char *command, char **argv, int argc)
 {
-    fprintf(w, "Counters cleared\r\n");
+    cli_print(cli, " _\n"
+                     "//\\\n"
+                     "V  \\\n"
+                     " \\  \\_\n"
+                     "  \\,'.`-.\n"
+                     "   |\\ `. `.\n"
+                     "   ( \\  `. `-.                        _,.-:\\\n"
+                     "    \\ \\   `.  `-._             __..--' ,-';/\n"
+                     "     \\ `.   `-.   `-..___..---'   _.--' ,'/\n"
+                     "      `. `.    `-._        __..--'    ,' /\n"
+                     "        `. `-_     ``--..''       _.-' ,'\n"
+                     "          `-_ `-.___        __,--'   ,'\n"
+                     "             `-.__  `----\"\"\"    __.-'\n"
+                     "hh                `--..____..--'");
+
+    return CLI_OK;
+}
+
+int cmd_clear_counters(struct cli_def *cli, char *command, char **argv, int argc)
+{
+    cli_print(cli, "Counters cleared");
     SET_STAT(last_reset, time(NULL));
     return CLI_OK;
 }
 
-int cmd_drop_user(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+int cmd_drop_user(struct cli_def *cli, char *command, char **argv, int argc)
 {
     int i;
     sessionidt s;
 
     if (!argc)
     {
-       fprintf(w, "Specify a user to drop\r\n");
+       cli_print(cli, "Specify a user to drop");
        return CLI_OK;
     }
     for (i = 0; i < argc; i++)
     {
        if (strchr(argv[i], '?'))
        {
-           fprintf(w, "username ...");
+           cli_print(cli, "username ...");
            return CLI_OK;
        }
     }
@@ -528,7 +720,7 @@ int cmd_drop_user(struct cli_def *cli, FILE *w, char *command, char **argv, int
     {
        if (!(s = sessionbyuser(argv[i])))
        {
-           fprintf(w, "User %s is not connected\r\n", argv[i]);
+           cli_print(cli, "User %s is not connected", argv[i]);
            continue;
        }
 
@@ -536,7 +728,7 @@ int cmd_drop_user(struct cli_def *cli, FILE *w, char *command, char **argv, int
        {
            int x;
 
-           fprintf(w, "Dropping user %s\r\n", session[s].user);
+           cli_print(cli, "Dropping user %s", session[s].user);
            for (x = 0; x < MAXSESSION; x++)
            {
                if (!cli_session_kill[x])
@@ -551,21 +743,21 @@ int cmd_drop_user(struct cli_def *cli, FILE *w, char *command, char **argv, int
     return CLI_OK;
 }
 
-int cmd_drop_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+int cmd_drop_tunnel(struct cli_def *cli, char *command, char **argv, int argc)
 {
     int i;
     tunnelidt tid;
 
     if (!argc)
     {
-       fprintf(w, "Specify a tunnel to drop\r\n");
+       cli_print(cli, "Specify a tunnel to drop");
        return CLI_OK;
     }
     for (i = 0; i < argc; i++)
     {
        if (strchr(argv[i], '?'))
        {
-           fprintf(w, "tunnel_id ...");
+           cli_print(cli, "tunnel_id ...");
            return CLI_OK;
        }
     }
@@ -576,19 +768,19 @@ int cmd_drop_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, in
 
        if ((tid = atol(argv[i])) <= 0 || (tid > MAXTUNNEL))
        {
-           fprintf(w, "Invalid tunnel ID (%d - %d)\r\n", 0, MAXTUNNEL);
+           cli_print(cli, "Invalid tunnel ID (%d - %d)", 0, MAXTUNNEL);
            continue;
        }
 
        if (!tunnel[tid].ip)
        {
-           fprintf(w, "Tunnel %d is not connected\r\n", tid);
+           cli_print(cli, "Tunnel %d is not connected", tid);
            continue;
        }
 
        if (tunnel[tid].die)
        {
-           fprintf(w, "Tunnel %d is already being shut down\r\n", tid);
+           cli_print(cli, "Tunnel %d is already being shut down", tid);
            continue;
        }
 
@@ -597,7 +789,7 @@ int cmd_drop_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, in
            if (!cli_tunnel_kill[x])
            {
                cli_tunnel_kill[x] = tid;
-               fprintf(w, "Tunnel %d shut down (%s)\r\n", tid, tunnel[tid].hostname);
+               cli_print(cli, "Tunnel %d shut down (%s)", tid, tunnel[tid].hostname);
                break;
            }
        }
@@ -606,21 +798,21 @@ int cmd_drop_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, in
     return CLI_OK;
 }
 
-int cmd_drop_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+int cmd_drop_session(struct cli_def *cli, char *command, char **argv, int argc)
 {
     int i;
     sessionidt s;
 
     if (!argc)
     {
-       fprintf(w, "Specify a session id to drop\r\n");
+       cli_print(cli, "Specify a session id to drop");
        return CLI_OK;
     }
     for (i = 0; i < argc; i++)
     {
        if (strchr(argv[i], '?'))
        {
-           fprintf(w, "session_id ...");
+           cli_print(cli, "session_id ...");
            return CLI_OK;
        }
     }
@@ -629,11 +821,11 @@ int cmd_drop_session(struct cli_def *cli, FILE *w, char *command, char **argv, i
     {
        if ((s = atol(argv[i])) <= 0 || (s > MAXSESSION))
        {
-           fprintf(w, "Invalid session ID (%d - %d)\r\n", 0, MAXSESSION);
+           cli_print(cli, "Invalid session ID (%d - %d)", 0, MAXSESSION);
            continue;
        }
 
-       if (session[s].ip && session[s].opened && !session[s].die)
+       if (session[s].opened && !session[s].die)
        {
            int x;
            for (x = 0; x < MAXSESSION; x++)
@@ -644,32 +836,32 @@ int cmd_drop_session(struct cli_def *cli, FILE *w, char *command, char **argv, i
                    break;
                }
            }
-           fprintf(w, "Dropping session %d\r\n", s);
+           cli_print(cli, "Dropping session %d", s);
        }
        else
        {
-           fprintf(w, "Session %d is not active.\r\n", s);
+           cli_print(cli, "Session %d is not active.", s);
        }
     }
 
     return CLI_OK;
 }
 
-int cmd_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+int cmd_snoop(struct cli_def *cli, char *command, char **argv, int argc)
 {
     int i;
     sessionidt s;
 
     if (!argc)
     {
-       fprintf(w, "Specify a user\r\n");
+       cli_print(cli, "Specify a user");
        return CLI_OK;
     }
     for (i = 0; i < argc; i++)
     {
        if (strchr(argv[i], '?'))
        {
-           fprintf(w, "username ...");
+           cli_print(cli, "username ...");
            return CLI_OK;
        }
     }
@@ -678,31 +870,31 @@ int cmd_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int argc
     {
        if (!(s = sessionbyuser(argv[i])))
        {
-           fprintf(w, "User %s is not connected\r\n", argv[i]);
+           cli_print(cli, "User %s is not connected", argv[i]);
            continue;
        }
        session[s].snoop = 1;
 
-       fprintf(w, "Snooping user %s\r\n", argv[i]);
+       cli_print(cli, "Snooping user %s", argv[i]);
     }
     return CLI_OK;
 }
 
-int cmd_no_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+int cmd_no_snoop(struct cli_def *cli, char *command, char **argv, int argc)
 {
     int i;
     sessionidt s;
 
     if (!argc)
     {
-       fprintf(w, "Specify a user\r\n");
+       cli_print(cli, "Specify a user");
        return CLI_OK;
     }
     for (i = 0; i < argc; i++)
     {
        if (strchr(argv[i], '?'))
        {
-           fprintf(w, "username ...");
+           cli_print(cli, "username ...");
            return CLI_OK;
        }
     }
@@ -711,31 +903,31 @@ int cmd_no_snoop(struct cli_def *cli, FILE *w, char *command, char **argv, int a
     {
        if (!(s = sessionbyuser(argv[i])))
        {
-           fprintf(w, "User %s is not connected\r\n", argv[i]);
+           cli_print(cli, "User %s is not connected", argv[i]);
            continue;
        }
        session[s].snoop = 0;
 
-       fprintf(w, "Not snooping user %s\r\n", argv[i]);
+       cli_print(cli, "Not snooping user %s", argv[i]);
     }
     return CLI_OK;
 }
 
-int cmd_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+int cmd_throttle(struct cli_def *cli, char *command, char **argv, int argc)
 {
     int i;
     sessionidt s;
 
     if (!argc)
     {
-       fprintf(w, "Specify a user\r\n");
+       cli_print(cli, "Specify a user");
        return CLI_OK;
     }
     for (i = 0; i < argc; i++)
     {
        if (strchr(argv[i], '?'))
        {
-           fprintf(w, "username ...");
+           cli_print(cli, "username ...");
            return CLI_OK;
        }
     }
@@ -744,31 +936,31 @@ int cmd_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, int a
     {
        if (!(s = sessionbyuser(argv[i])))
        {
-           fprintf(w, "User %s is not connected\r\n", argv[i]);
+           cli_print(cli, "User %s is not connected", argv[i]);
            continue;
        }
        throttle_session(s, 1);
 
-       fprintf(w, "throttling user %s\r\n", argv[i]);
+       cli_print(cli, "throttling user %s", argv[i]);
     }
     return CLI_OK;
 }
 
-int cmd_no_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+int cmd_no_throttle(struct cli_def *cli, char *command, char **argv, int argc)
 {
     int i;
     sessionidt s;
 
     if (!argc)
     {
-       fprintf(w, "Specify a user\r\n");
+       cli_print(cli, "Specify a user");
        return CLI_OK;
     }
     for (i = 0; i < argc; i++)
     {
        if (strchr(argv[i], '?'))
        {
-           fprintf(w, "username ...");
+           cli_print(cli, "username ...");
            return CLI_OK;
        }
     }
@@ -777,30 +969,30 @@ int cmd_no_throttle(struct cli_def *cli, FILE *w, char *command, char **argv, in
        {
        if (!(s = sessionbyuser(argv[i])))
        {
-           fprintf(w, "User %s is not connected\r\n", argv[i]);
+           cli_print(cli, "User %s is not connected", argv[i]);
            continue;
        }
        throttle_session(s, 0);
 
-       fprintf(w, "unthrottling user %s\r\n", argv[i]);
+       cli_print(cli, "unthrottling user %s", argv[i]);
     }
     return CLI_OK;
 }
 
-int cmd_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+int cmd_debug(struct cli_def *cli, char *command, char **argv, int argc)
 {
     int i;
 
     if (!argc)
     {
-       fprintf(w, "Currently debugging: ");
-       if (debug_flags.critical) fprintf(w, "critical ");
-       if (debug_flags.error) fprintf(w, "error ");
-       if (debug_flags.warning) fprintf(w, "warning ");
-       if (debug_flags.info) fprintf(w, "info ");
-       if (debug_flags.calls) fprintf(w, "calls ");
-       if (debug_flags.data) fprintf(w, "data ");
-       fprintf(w, "\r\n");
+       cli_print(cli, "Currently debugging: ");
+       if (debug_flags.critical) cli_print(cli, "critical ");
+       if (debug_flags.error) cli_print(cli, "error ");
+       if (debug_flags.warning) cli_print(cli, "warning ");
+       if (debug_flags.info) cli_print(cli, "info ");
+       if (debug_flags.calls) cli_print(cli, "calls ");
+       if (debug_flags.data) cli_print(cli, "data ");
+       cli_print(cli, "");
        return CLI_OK;
     }
 
@@ -808,13 +1000,13 @@ int cmd_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int argc
     {
        if (*argv[i] == '?')
        {
-           fprintf(w, "Possible debugging states are:\r\n");
-           fprintf(w, "        critical\r\n");
-           fprintf(w, "        error\r\n");
-           fprintf(w, "        warning\r\n");
-           fprintf(w, "        info\r\n");
-           fprintf(w, "        calls\r\n");
-           fprintf(w, "        data\r\n");
+           cli_print(cli, "Possible debugging states are:");
+           cli_print(cli, "    critical");
+           cli_print(cli, "    error");
+           cli_print(cli, "    warning");
+           cli_print(cli, "    info");
+           cli_print(cli, "    calls");
+           cli_print(cli, "    data");
            return CLI_OK;
        }
     }
@@ -837,7 +1029,7 @@ int cmd_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int argc
     return CLI_OK;
 }
 
-int cmd_no_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+int cmd_no_debug(struct cli_def *cli, char *command, char **argv, int argc)
 {
     int i;
 
@@ -855,49 +1047,205 @@ int cmd_no_debug(struct cli_def *cli, FILE *w, char *command, char **argv, int a
     return CLI_OK;
 }
 
-int cmd_watch_session(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+int cmd_watch_session(struct cli_def *cli, char *command, char **argv, int argc)
 {
     sessionidt s;
 
     if (argc != 1)
     {
-       fprintf(w, "Specify a single session to debug (0 to disable)\r\n");
+       cli_print(cli, "Specify a single session to debug (0 to disable)");
        return CLI_OK;
     }
     s = atoi(argv[0]);
 
     if (debug_session)
-       fprintf(w, "No longer debugging session %d\r\n", debug_session);
+       cli_print(cli, "No longer debugging session %d", debug_session);
 
-    if (s) fprintf(w, "Debugging session %d.\r\n", s);
+    if (s) cli_print(cli, "Debugging session %d.", s);
     debug_session = s;
 
     return CLI_OK;
 }
 
-int cmd_watch_tunnel(struct cli_def *cli, FILE *w, char *command, char **argv, int argc)
+int cmd_watch_tunnel(struct cli_def *cli, char *command, char **argv, int argc)
 {
     tunnelidt s;
 
     if (argc != 1)
     {
-       fprintf(w, "Specify a single tunnel to debug (0 to disable)\r\n");
+       cli_print(cli, "Specify a single tunnel to debug (0 to disable)");
        return CLI_OK;
     }
     s = atoi(argv[0]);
 
     if (debug_tunnel)
-       fprintf(w, "No longer debugging tunnel %d\r\n", debug_tunnel);
+       cli_print(cli, "No longer debugging tunnel %d", debug_tunnel);
 
-    if (s) fprintf(w, "Debugging tunnel %d.\r\n", s);
+    if (s) cli_print(cli, "Debugging tunnel %d.", s);
     debug_tunnel = s;
 
     return CLI_OK;
 }
 
-int regular_stuff(struct cli_def *cli, FILE *w)
+int cmd_load_plugin(struct cli_def *cli, char *command, char **argv, int argc)
+{
+    int i, firstfree = 0;
+    if (argc != 1)
+    {
+       cli_print(cli, "Specify a plugin to load");
+       return CLI_OK;
+    }
+
+    for (i = 0; i < MAXPLUGINS; i++)
+    {
+       if (!*config->plugins[i] && !firstfree)
+           firstfree = i;
+       if (strcmp(config->plugins[i], argv[0]) == 0)
+       {
+           cli_print(cli, "Plugin is already loaded");
+           return CLI_OK;
+       }
+    }
+
+    if (firstfree)
+    {
+       strncpy(config->plugins[firstfree], argv[0], sizeof(config->plugins[firstfree]) - 1);
+       config->reload_config = 1;
+       cli_print(cli, "Loading plugin %s", argv[0]);
+    }
+
+    return CLI_OK;
+}
+
+int cmd_remove_plugin(struct cli_def *cli, char *command, char **argv, int argc)
+{
+    int i;
+
+    if (argc != 1)
+    {
+       cli_print(cli, "Specify a plugin to remove");
+       return CLI_OK;
+    }
+
+    for (i = 0; i < MAXPLUGINS; i++)
+    {
+       if (strcmp(config->plugins[i], argv[0]) == 0)
+       {
+           config->reload_config = 1;
+           memset(config->plugins[i], 0, sizeof(config->plugins[i]));
+           return CLI_OK;
+       }
+    }
+
+    cli_print(cli, "Plugin is not loaded");
+    return CLI_OK;
+}
+
+char *duration(time_t seconds)
+{
+    static char *buf = NULL;
+    if (!buf) buf = calloc(64, 1);
+
+    if (seconds > 86400)
+       sprintf(buf, "%d days", (int)(seconds / 86400.0));
+    else if (seconds > 60)
+       sprintf(buf, "%02d:%02lu", (int)(seconds / 3600.0), seconds % 60);
+    else
+       sprintf(buf, "%lu sec", seconds);
+    return buf;
+}
+
+int cmd_uptime(struct cli_def *cli, char *command, char **argv, int argc)
+{
+    FILE *fh;
+    char buf[100], *p = buf, *loads[3];
+    int i, num_sessions = 0;
+    time_t time_now;
+
+    fh = fopen("/proc/loadavg", "r");
+    fgets(buf, 100, fh);
+    fclose(fh);
+
+    for (i = 0; i < 3; i++)
+       loads[i] = strdup(strsep(&p, " "));
+
+    time(&time_now);
+    strftime(buf, 99, "%H:%M:%S", localtime(&time_now));
+
+    for (i = 1; i < MAXSESSION; i++)
+       if (session[i].opened) num_sessions++;
+
+    cli_print(cli, "%s up %s, %d users, load average: %s, %s, %s",
+           buf,
+           duration(abs(time_now - config->start_time)),
+           num_sessions,
+           loads[0], loads[1], loads[2]
+    );
+    for (i = 0; i < 3; i++)
+       if (loads[i]) free(loads[i]);
+
+    cli_print(cli, "Bandwidth: %s", config->bandwidth);
+
+    return CLI_OK;
+}
+
+int cmd_set(struct cli_def *cli, char *command, char **argv, int argc)
+{
+    int i;
+
+    if (argc != 2)
+    {
+       cli_print(cli, "Usage: set <variable> <value>");
+       return CLI_OK;
+    }
+
+    for (i = 0; config_values[i].key; i++)
+    {
+       void *value = ((void *)config) + config_values[i].offset;
+       if (strcmp(config_values[i].key, argv[0]) == 0)
+       {
+           // Found a value to set
+           cli_print(cli, "Setting \"%s\" to \"%s\"", argv[0], argv[1]);
+           switch (config_values[i].type)
+           {
+               case STRING:
+                   strncpy((char *)value, argv[1], config_values[i].size - 1);
+                   break;
+               case INT:
+                   *(int *)value = atoi(argv[1]);
+                   break;
+               case UNSIGNED_LONG:
+                   *(unsigned long *)value = atol(argv[1]);
+                   break;
+               case SHORT:
+                   *(short *)value = atoi(argv[1]);
+                   break;
+               case IP:
+                   *(unsigned *)value = inet_addr(argv[1]);
+                   break;
+               case BOOL:
+                   if (strcasecmp(argv[1], "yes") == 0 || strcasecmp(argv[1], "true") == 0 || strcasecmp(argv[1], "1") == 0)
+                       *(int *)value = 1;
+                   else
+                       *(int *)value = 0;
+                   break;
+               default:
+                   cli_print(cli, "Unknown variable type");
+                   break;
+           }
+           config->reload_config = 1;
+           return CLI_OK;
+       }
+    }
+
+    cli_print(cli, "Unknown variable \"%s\"", argv[0]);
+    return CLI_OK;
+}
+
+int regular_stuff(struct cli_def *cli)
 {
     int i = debug_rb_tail;
+    int reprompt = 0;
 
 #ifdef RINGBUFFER
     while (i != ringbuffer->tail)
@@ -924,12 +1272,14 @@ int regular_stuff(struct cli_def *cli, FILE *w)
            memcpy(&addr, &address, sizeof(ringbuffer->buffer[i].address));
            ipaddr = inet_ntoa(addr);
 
-           fprintf(w, "%s-%s-%u-%u %s\r",
+           cli_print(cli, "\r%s-%s-%u-%u %s",
                    debug_levels[(int)ringbuffer->buffer[i].level],
                    ipaddr,
                    ringbuffer->buffer[i].tunnel,
                    ringbuffer->buffer[i].session,
                    ringbuffer->buffer[i].message);
+
+           reprompt = 1;
        }
 
        if (++i == ringbuffer->tail) break;
@@ -937,8 +1287,8 @@ int regular_stuff(struct cli_def *cli, FILE *w)
     }
 
     debug_rb_tail = ringbuffer->tail;
+    if (reprompt)
+       cli_reprompt(cli);
 #endif
     return CLI_OK;
 }
-
-#endif
index 53e65cc..9a988e2 100644 (file)
--- a/cluster.c
+++ b/cluster.c
@@ -1,5 +1,5 @@
 // L2TPNS Clustering Stuff
-// $Id: cluster.c,v 1.1 2003/12/16 07:07:39 fred_nerk Exp $ #include <stdio.h> #include <sys/file.h>
+// $Id: cluster.c,v 1.2 2004/03/05 00:09:03 fred_nerk Exp $ #include <stdio.h> #include <sys/file.h>
 
 #include <sys/stat.h>
 #include <sys/types.h>
index 8b92918..23ce345 100644 (file)
--- a/cluster.h
+++ b/cluster.h
@@ -1,11 +1,12 @@
 // L2TPNS Clustering Stuff
-// $Id: cluster.h,v 1.1 2003/12/16 07:07:39 fred_nerk Exp $
+// $Id: cluster.h,v 1.2 2004/03/05 00:09:03 fred_nerk Exp $
 
 #define C_HELLO                        1
 #define C_HELLO_RESPONSE       2
 #define C_PING                 3
 #define C_TUNNEL               4
 #define C_SESSION              5
+#define C_GOODBYE              6
 
 #define CLUSTERPORT            32792
 #define CLUSTERCLIENTPORT      32793
index c2bc501..9133531 100644 (file)
@@ -1,5 +1,5 @@
 // L2TPNS Cluster Master
-// $Id: cluster_master.c,v 1.1 2003/12/16 07:07:39 fred_nerk Exp $
+// $Id: cluster_master.c,v 1.2 2004/03/05 00:09:03 fred_nerk Exp $
 
 #include <stdio.h>
 #include <netinet/in.h>
@@ -51,6 +51,7 @@ int handle_hello(char *buf, int l, struct sockaddr_in *src_addr, uint32_t addr);
 int handle_tunnel(char *buf, int l, uint32_t addr);
 int handle_session(char *buf, int l, uint32_t addr);
 int handle_ping(char *buf, int l, uint32_t addr);
+int handle_goodbye(char *buf, int l, uint32_t addr);
 int backup_up(slave *s);
 int backup_down(slave *s);
 int return_state(slave *s);
@@ -91,7 +92,7 @@ int main(int argc, char *argv[])
 
     signal(SIGCHLD, sigchild_handler);
 
-    log(0, "Cluster Manager $Id: cluster_master.c,v 1.1 2003/12/16 07:07:39 fred_nerk Exp $ starting\n");
+    log(0, "Cluster Manager $Id: cluster_master.c,v 1.2 2004/03/05 00:09:03 fred_nerk Exp $ starting\n");
 
     to.tv_sec = 1;
     to.tv_usec = 0;
@@ -156,6 +157,7 @@ int main(int argc, char *argv[])
 
 int processmsg(char *buf, int l, struct sockaddr_in *src_addr)
 {
+    slave *s;
     char mtype;
     uint32_t addr;
 
@@ -168,6 +170,16 @@ int processmsg(char *buf, int l, struct sockaddr_in *src_addr)
 
     mtype = *buf; buf++; l--;
 
+    if (mtype != C_GOODBYE && (s = find_slave(addr)) && s->down)
+    {
+       char *hostname;
+       hostname = calloc(l + 1, 1);
+       memcpy(hostname, buf, l);
+       log(1, "Slave \"%s\" (for %s) has come back.\n", hostname, inet_toa(s->ip_address));
+       backup_down(s);
+       free(hostname);
+    }
+
     switch (mtype)
     {
        case C_HELLO:
@@ -187,6 +199,10 @@ int processmsg(char *buf, int l, struct sockaddr_in *src_addr)
            if (!find_slave(addr)) handle_hello((char *)(buf + 1), *(char *)buf, src_addr, addr);
            handle_session(buf, l, addr);
            break;
+       case C_GOODBYE:
+           if (!find_slave(addr)) break;
+           handle_goodbye(buf, l, addr);
+           break;
     }
     return mtype;
 }
@@ -478,3 +494,24 @@ int backup_down(slave *s)
     return 0;
 }
 
+int handle_goodbye(char *buf, int l, uint32_t addr)
+{
+    int i;
+    slave *s;
+
+    // Is this a slave we have state information for?
+    if ((s = find_slave(addr)))
+    {
+        log(0, "Received goodbye for slave %s\n", s->hostname);
+       ll_delete(slaves, s);
+       for (i = 0; i < s->num_tunnels; i++)
+           if (s->tunnels[i]) free(s->tunnels[i]);
+       for (i = 0; i < s->num_sessions; i++)
+           if (s->sessions[i]) free(s->sessions[i]);
+       if (s->hostname) free(s->hostname);
+       free(s);
+    }
+
+    return 0;
+}
+
index df719a0..cce978a 100644 (file)
@@ -1,5 +1,5 @@
 // L2TPNS Cluster Master
-// $Id: cluster_slave.c,v 1.1 2003/12/16 07:07:39 fred_nerk Exp $
+// $Id: cluster_slave.c,v 1.2 2004/03/05 00:09:03 fred_nerk Exp $
 
 #include <stdio.h>
 #include <netinet/in.h>
 #include "ll.h"
 #include "util.h"
 
+// vim: sw=4 ts=8
+
 extern int cluster_sockfd;
-extern tunnelt *tunnel;
-extern sessiont *session;
-extern uint32_t cluster_address;
 extern char hostname[1000];
-extern int debug;
 extern ippoolt *ip_address_pool;
 extern uint32_t vip_address;
-extern tunnelidt tunnelfree;
-extern sessionidt sessionfree;
+extern struct configt *config;
 
 int handle_tunnel(char *buf, int l);
 int handle_session(char *buf, int l);
@@ -78,6 +75,13 @@ int handle_tunnel(char *buf, int l)
 {
     int t;
 
+    // Ignore tunnel message if NOSTATEFILE exists
+    if (config->ignore_cluster_updates)
+    {
+       log(1, 0, 0, 0, "Discarding tunnel message from cluster master.\n", l, sizeof(tunnelt));
+       return 0;
+    }
+
     t = *(int *)buf;
     log(1, 0, 0, t, "Receiving tunnel %d from cluster master (%d bytes)\n", t, l);
     buf += sizeof(int); l -= sizeof(int);
@@ -94,16 +98,6 @@ int handle_tunnel(char *buf, int l)
        return 0;
     }
 
-    if (t > 1)
-    {
-       tunnel[t-1].next = tunnel[t].next;
-    }
-
-    if (tunnelfree == t)
-    {
-       tunnelfree = tunnel[t].next;
-    }
-
     memcpy(&tunnel[t], buf, l);
     log(3, 0, 0, t, "Cluster master sent tunnel for %s\n", tunnel[t].hostname);
 
@@ -117,6 +111,13 @@ int handle_session(char *buf, int l)
 {
     int s;
 
+    // Ignore tunnel message if NOSTATEFILE exists
+    if (config->ignore_cluster_updates)
+    {
+       log(1, 0, 0, 0, "Discarding session message from cluster master.\n", l, sizeof(tunnelt));
+       return 0;
+    }
+
     s = *(int *)buf;
     log(1, 0, s, 0, "Receiving session %d from cluster master (%d bytes)\n", s, l);
     buf += sizeof(int); l -= sizeof(int);
@@ -163,6 +164,10 @@ int handle_session(char *buf, int l)
            }
        }
     }
+    /*
+    if (session[s].servicenet)
+       servicenet_session(s, 1);
+    */
     return 0;
 }
 
@@ -214,7 +219,7 @@ int cluster_send_session(int s)
        memcpy((char *)(packet + len), &session[s], sizeof(sessiont));
        len += sizeof(sessiont);
 
-       cluster_send_message(cluster_address, vip_address, C_SESSION, packet, len);
+       cluster_send_message(config->cluster_address, vip_address, C_SESSION, packet, len);
        free(packet);
 
        return 1;
@@ -241,7 +246,27 @@ int cluster_send_tunnel(int t)
        memcpy((char *)(packet + len), &tunnel[t], sizeof(tunnelt));
        len += sizeof(tunnelt);
 
-       cluster_send_message(cluster_address, vip_address, C_TUNNEL, packet, len);
+       cluster_send_message(config->cluster_address, vip_address, C_TUNNEL, packet, len);
+       free(packet);
+
+       return 1;
+}
+
+int cluster_send_goodbye()
+{
+       char *packet;
+       int len = 0;
+
+       packet = malloc(4096);
+
+       log(2, 0, 0, 0, "Sending goodbye to cluster master\n");
+       // Hostname
+       len = strlen(hostname);
+       *(char *)packet = len;
+       memcpy((char *)(packet + 1), hostname, len);
+       len++;
+
+       cluster_send_message(config->cluster_address, vip_address, C_GOODBYE, packet, len);
        free(packet);
 
        return 1;
index 064bea2..1083b65 100644 (file)
--- a/garden.c
+++ b/garden.c
 int __plugin_api_version = 1;
 struct pluginfuncs p;
 
-int garden_session(sessiont *s, int flag);
-
 char *init_commands[] = {
        // This is for incoming connections to a gardened user
        "iptables -t nat -N garden_users 2>&1 >/dev/null",
-       "iptables -t nat -F garden_users 2>&1 >/dev/null",
-       "iptables -t nat -N garden 2>&1 >/dev/null",
+       "iptables -t nat -F garden_users",
+       "iptables -t nat -N garden 2>&1", /* Don't flush - init script sets this up */
        "iptables -t nat -A l2tpns -j garden_users",
        NULL
 };
 
 char *done_commands[] = {
        "iptables -t nat -F garden_users 2>&1 >/dev/null",
-       "iptables -t nat -D l2tpns -j garden_users 2>&1 >/dev/null",
+       "iptables -t nat -D l2tpns -j garden_users",
        NULL
 };
 
+int garden_session(sessiont *s, int flag);
+
 int plugin_post_auth(struct param_post_auth *data)
 {
        // Ignore if user authentication was successful
        if (data->auth_allowed) return PLUGIN_RET_OK;
 
-       p.log(3, 0, 0, 0, "User allowed into walled garden\n");
+       p.log(3, 0, 0, 0, "Walled Garden allowing login\n");
        data->auth_allowed = 1;
-       data->s->walled_garden = 1;
+       data->s->garden = 1;
        return PLUGIN_RET_OK;
 }
 
 int plugin_new_session(struct param_new_session *data)
 {
-       if (data->s->walled_garden) garden_session(data->s, 1);
+       if (data->s->garden) garden_session(data->s, 1);
        return PLUGIN_RET_OK;
 }
 
 int plugin_kill_session(struct param_new_session *data)
 {
-       if (data->s->walled_garden) garden_session(data->s, 0);
+       if (data->s->garden) garden_session(data->s, 0);
        return PLUGIN_RET_OK;
 }
 
@@ -58,10 +58,7 @@ int plugin_control(struct param_control *data)
        if (data->type != PKT_GARDEN && data->type != PKT_UNGARDEN) return PLUGIN_RET_OK;
        if (!data->data && data->data_length) return PLUGIN_RET_OK;
        session = atoi((char*)(data->data));
-
-       if (!session)
-               return PLUGIN_RET_OK;
-
+       if (!session) return PLUGIN_RET_OK; // Really?
        data->send_response = 1;
        s = p.get_session_by_id(session);
        if (!s || !s->ip)
@@ -86,11 +83,6 @@ int plugin_control(struct param_control *data)
        return PLUGIN_RET_STOP;
 }
 
-int plugin_config(struct param_config *data)
-{
-       return PLUGIN_RET_OK;
-}
-
 int garden_session(sessiont *s, int flag)
 {
        char cmd[2048];
@@ -98,25 +90,48 @@ int garden_session(sessiont *s, int flag)
        if (!s) return 0;
        if (!s->opened) return 0;
 
+       /* Note that we don't handle throttling/snooping/etc here
+        * To do that, we'd need to send an end accounting record
+        * then a radius auth, then start accouting again.
+        * That means that we need the password (which garden has)
+        * and a lot of code to check that the new set of params
+        * (routes, IP, ACLs, etc) 'matched' the old one in a
+        * 'compatable' way. (ie user's system doesn't need to be told
+        * of the change)
+        *
+        * Thats a lot of pain/code for very little gain.
+        * If we want them redone from scratch, just sessionkill them -
+        * a user on garden isn't going to have any open TCP
+        * connections which are worth caring about, anyway.
+        *
+        * Note that the user will be rethrottled shortly by the scan
+        * script thingy if appropriate.
+        *
+        * Currently, garden only directly ungardens someone if
+        * they haven't paid their bill, and then subsequently do so
+        * online. This isn't something which can be set up by a malicious
+        * customer at will.
+        */
        if (flag == 1)
        {
+               // Gardened User
                p.log(2, 0, 0, s->tunnel, "Trap user %s (%s) in walled garden\n", s->user, p.inet_toa(ntohl(s->ip)));
                snprintf(cmd, 2048, "iptables -t nat -A garden_users -s %s -j garden", p.inet_toa(ntohl(s->ip)));
                p.log(3, 0, 0, s->tunnel, "%s\n", cmd);
                system(cmd);
-               s->walled_garden = 1;
+               s->garden = 1;
        }
        else
        {
                sessionidt other;
-               int count = 10;
+               int count = 40;
 
                // Normal User
                p.log(2, 0, 0, s->tunnel, "Release user %s (%s) from walled garden\n", s->user, p.inet_toa(ntohl(s->ip)));
                // Kick off any duplicate usernames
                // but make sure not to kick off ourself
                if (s->ip && !s->die && (other = p.get_session_by_username(s->user)) && s != p.get_session_by_id(other)) {
-                       p.sessionkill(other, "Duplicate session when user ungardened");
+                       p.sessionkill(other, "Duplicate session when user un-gardened");
                }
                /* Clean up counters */
                s->cin = s->cout = 0;
@@ -130,7 +145,7 @@ int garden_session(sessiont *s, int flag)
                        if (WEXITSTATUS(status) != 0) break;
                }
 
-               s->walled_garden = 0;
+               s->garden = 0;
 
                if (!s->die) {
                        /* OK, we're up! */
@@ -138,7 +153,7 @@ int garden_session(sessiont *s, int flag)
                        p.radiussend(r, RADIUSSTART);
                }
        }
-       s->walled_garden = flag;
+       s->garden = flag;
        return 1;
 }
 
@@ -149,11 +164,9 @@ int plugin_init(struct pluginfuncs *funcs)
        if (!funcs) return 0;
        memcpy(&p, funcs, sizeof(p));
 
-       p.log(1, 0, 0, 0, "Enabling walled garden service\n");
-
        for (i = 0; init_commands[i] && *init_commands[i]; i++)
        {
-               p.log(4, 0, 0, 0, "Running %s\n", init_commands[i]);
+               p.log(3, 0, 0, 0, "Running %s\n", init_commands[i]);
                system(init_commands[i]);
        }
 
@@ -165,7 +178,7 @@ void plugin_done()
        int i;
        for (i = 0; done_commands[i] && *done_commands[i]; i++)
        {
-               p.log(4, 0, 0, 0, "Running %s\n", done_commands[i]);
+               p.log(3, 0, 0, 0, "Running %s\n", done_commands[i]);
                system(done_commands[i]);
        }
 }
diff --git a/icmp.c b/icmp.c
new file mode 100644 (file)
index 0000000..0a56740
--- /dev/null
+++ b/icmp.c
@@ -0,0 +1,86 @@
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <linux/ip.h>
+#include <linux/icmp.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <memory.h>
+#include "l2tpns.h"
+
+extern ipt myip;
+
+__u16 _checksum(unsigned char *addr, int count);
+
+void host_unreachable(ipt destination, u16 id, ipt source, char *packet, int packet_len)
+{
+       char buf[128] = {0};
+       struct iphdr *iph;
+       struct icmphdr *icmp;
+       char *data;
+       int len = 0, on = 1, icmp_socket;
+       struct sockaddr_in whereto = {0};
+
+       if (!(icmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)))
+               return;
+       setsockopt(icmp_socket, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on));
+
+       whereto.sin_addr.s_addr = destination;
+       whereto.sin_family = AF_INET;
+
+       iph = (struct iphdr *)(buf);
+       len = sizeof(struct iphdr);
+       icmp = (struct icmphdr *)(buf + len);
+       len += sizeof(struct icmphdr);
+       data = (char *)(buf + len);
+       len += (packet_len < 64) ? packet_len : 64;
+       memcpy(data, packet, (packet_len < 64) ? packet_len : 64);
+
+       iph->tos = 0;
+       iph->id = id;
+       iph->frag_off = 0;
+       iph->ttl = 30;
+       iph->check = 0;
+       iph->version = 4;
+       iph->ihl = 5;
+       iph->protocol = 1;
+       iph->check = 0;
+       iph->daddr = destination;
+       iph->saddr = source;
+
+       iph->tot_len = ntohs(len);
+
+       icmp->type = ICMP_DEST_UNREACH;
+       icmp->code = ICMP_HOST_UNREACH;
+       icmp->checksum = _checksum((char *)icmp, sizeof(struct icmphdr) + ((packet_len < 64) ? packet_len : 64));
+
+       iph->check = _checksum((char *)iph, sizeof(struct iphdr));
+
+       sendto(icmp_socket, (char *)buf, len, 0, (struct sockaddr *)&whereto, sizeof(struct sockaddr));
+       close(icmp_socket);
+}
+
+__u16 _checksum(unsigned char *addr, int count)
+{
+       register long sum = 0;
+
+       for (; count > 1; count -= 2)
+       {
+               sum += ntohs(*(u32 *)addr);
+               addr += 2;
+       }
+
+       if (count > 1) sum += *(unsigned char *)addr;
+
+       // take only 16 bits out of the 32 bit sum and add up the carries
+       while (sum >> 16)
+               sum = (sum & 0xFFFF) + (sum >> 16);
+
+       // one's complement the result
+       sum = ~sum;
+
+       return htons((u16) sum);
+}
index ea21dda..b0fe1d8 100644 (file)
--- a/l2tpns.c
+++ b/l2tpns.c
@@ -8,6 +8,8 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/if_tun.h>
+#define SYSLOG_NAMES
+#include <syslog.h>
 #include <malloc.h>
 #include <math.h>
 #include <net/route.h>
@@ -18,6 +20,7 @@
 #include <stdarg.h>
 #include <stdlib.h>
 #include <stdio.h>
+#define __USE_GNU
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <linux/if.h>
+#include <stddef.h>
 #include <time.h>
 #include <dlfcn.h>
 #include <unistd.h>
-#ifdef HAVE_LIBCLI
-#include <libcli.h>
-#endif
 #include "md5.h"
 #include "l2tpns.h"
 #include "cluster.h"
 #include "control.h"
 #include "util.h"
 
-ipt radiusserver[MAXRADSERVER]; // radius servers
-u8 numradiusservers = 0;  // how many radius servers
-
 // Globals
-char tapdevice[10] = "";    // tap device name
-int tapfd = -1;            // tap interface file handle
-int udpfd = -1;            // UDP file handle
-int controlfd = -1;        // Control signal handle
-int snoopfd = -1;          // UDP file handle for sending out intercept data
-int radfd = -1;            // RADIUS requests file handle
-int ifrfd = -1;            // File descriptor for routing, etc
-char debug = 0;                // debug leveL
-time_t basetime = 0;          // base clock
-char hostname[1000] = ""; // us.
-ipt myip = 0;              // MY IP
-u16 tapmac[3];             // MAC of tap interface
-int tapidx;               // ifr_ifindex of tap device
-char *radiussecret = 0;      // RADIUS secret
-char *l2tpsecret = 0;        // L2TP secret
-u32 sessionid = 0;         // session id for radius accounting
-char   *snoop_destination_host = NULL;
-u16    snoop_destination_port = 0;
-char *log_filename = NULL;
-char *config_file = CONFIGFILE;
+struct configt *config = NULL; // all configuration
+int tapfd = -1;                        // tap interface file handle
+int udpfd = -1;                        // UDP file handle
+int controlfd = -1;            // Control signal handle
+int snoopfd = -1;              // UDP file handle for sending out intercept data
+int radfd = -1;                        // RADIUS requests file handle
+int ifrfd = -1;                        // File descriptor for routing, etc
+time_t basetime = 0;           // base clock
+char hostname[1000] = "";      // us.
+ipt myip = 0;                  // MY IP
+u16 tapmac[3];                 // MAC of tap interface
+int tapidx;                    // ifr_ifindex of tap device
+u32 sessionid = 0;             // session id for radius accounting
+int syslog_log = 0;            // are we logging to syslog
 FILE *log_stream = NULL;
-unsigned long default_dns1 = 0, default_dns2 = 0;
 struct sockaddr_in snoop_addr = {0};
-extern unsigned long rl_rate;
 extern int cluster_sockfd;
 unsigned long last_sid = 0;
-int config_save_state = 0;
-int radius_accounting = 0;
-char *accounting_dir = NULL;
-uint32_t cluster_address = 0;
-uint32_t bind_address = INADDR_ANY;
 int handle_interface = 0;
-#ifdef HAVE_LIBCLI
-pid_t cli_pid = 0;
 int clifd = 0;
 sessionidt *cli_session_kill = NULL;
 tunnelidt *cli_tunnel_kill = NULL;
-#endif
 static void *ip_hash[256];
 unsigned long udp_tx = 0, udp_rx = 0, udp_rx_pkt = 0;
 unsigned long eth_tx = 0, eth_rx = 0, eth_rx_pkt = 0;
-unsigned int ip_pool_index = 0;
 unsigned int ip_pool_size = 0;
 time_t time_now;
 char time_now_string[64] = {0};
 char main_quit = 0;
-int dump_speed = 0;
-int target_uid = 500;
 char *_program_name = NULL;
 linked_list *loaded_plugins;
 linked_list *plugins[MAX_PLUGIN_TYPES];
 
+#define membersize(STRUCT, MEMBER) sizeof(((STRUCT *)0)->MEMBER)
+#define CONFIG(NAME, MEMBER, TYPE) { NAME, offsetof(struct configt, MEMBER), membersize(struct configt, MEMBER), TYPE }
+
+struct config_descriptt config_values[] = {
+       CONFIG("debug", debug, INT),
+       CONFIG("log_file", log_filename, STRING),
+       CONFIG("l2tp_secret", l2tpsecret, STRING),
+       CONFIG("primary_dns", default_dns1, IP),
+       CONFIG("secondary_dns", default_dns2, IP),
+       CONFIG("save_state", save_state, BOOL),
+       CONFIG("snoop_host", snoop_destination_host, IP),
+       CONFIG("snoop_port", snoop_destination_port, SHORT),
+       CONFIG("primary_radius", radiusserver[0], IP),
+       CONFIG("secondary_radius", radiusserver[1], IP),
+       CONFIG("radius_accounting", radius_accounting, BOOL),
+       CONFIG("radius_secret", radiussecret, STRING),
+       CONFIG("bind_address", bind_address, IP),
+       CONFIG("cluster_master", cluster_address, IP),
+       CONFIG("throttle_speed", rl_rate, UNSIGNED_LONG),
+       CONFIG("accounting_dir", accounting_dir, STRING),
+       CONFIG("setuid", target_uid, INT),
+       CONFIG("dump_speed", dump_speed, BOOL),
+       { NULL, 0, 0, 0 },
+};
+
 char *plugin_functions[] = {
        NULL,
        "plugin_pre_auth",
@@ -105,7 +109,6 @@ char *plugin_functions[] = {
        "plugin_packet_rx",
        "plugin_packet_tx",
        "plugin_timer",
-       "plugin_config",
        "plugin_new_session",
        "plugin_kill_session",
        "plugin_control",
@@ -113,13 +116,10 @@ char *plugin_functions[] = {
 };
 #define max_plugin_functions (sizeof(plugin_functions) / sizeof(char *))
 
-tunnelt *tunnel = NULL;        // 1000 * 45 = 45000 = 45k
+tunnelt *tunnel = NULL;                // 1000 * 45 = 45000 = 45k
 sessiont *session = NULL;      // 5000 * 213 = 1065000 = 1 Mb
 radiust *radius = NULL;
 ippoolt *ip_address_pool = NULL;
-tunnelidt tunnelfree;           // free list link heads
-sessionidt sessionfree = 0;
-u8 radiusfree;
 controlt *controlfree = 0;
 struct Tstats *_statistics = NULL;
 #ifdef RINGBUFFER
@@ -136,6 +136,9 @@ void sigsegv_handler(int);
 void read_config_file();
 void read_state();
 void dump_state();
+void tunnel_clean();
+tunnelidt new_tunnel();
+void update_config();
 
 // return internal time (10ths since run)
 clockt now(void)
@@ -154,6 +157,8 @@ clockt backoff(u8 try)
 
 void _log(int level, ipt address, sessionidt s, tunnelidt t, const char *format, ...)
 {
+       static char message[65535] = {0};
+       static char message2[65535] = {0};
        va_list ap;
 
 #ifdef RINGBUFFER
@@ -175,68 +180,73 @@ void _log(int level, ipt address, sessionidt s, tunnelidt t, const char *format,
        }
 #endif
 
-       if (debug < level) return;
+       if (config->debug < level) return;
 
-       if (!log_stream && log_filename)
+       va_start(ap, format);
+       if (log_stream)
        {
-               if ((log_stream = fopen(log_filename, "a")))
-               fseek(log_stream, 0, SEEK_END);
-               setbuf(log_stream, NULL);
+               vsnprintf(message2, 65535, format, ap);
+               snprintf(message, 65535, "%s %02d/%02d %s", time_now_string, t, s, message2);
+               fprintf(log_stream, message);
        }
-       if (!log_stream)
+       else if (syslog_log)
        {
-               log_stream = stderr;
-               setbuf(log_stream, NULL);
+               vsnprintf(message2, 65535, format, ap);
+               snprintf(message, 65535, "%02d/%02d %s", t, s, message2);
+               syslog(level + 2, message); // We don't need LOG_EMERG or LOG_ALERT
        }
-
-       va_start(ap, format);
-       fprintf(log_stream, "%s %02d/%02d ", time_now_string, t, s);
-       vfprintf(log_stream, format, ap);
        va_end(ap);
 }
 
 void _log_hex(int level, ipt address, sessionidt s, tunnelidt t, const char *title, const char *data, int maxsize)
 {
-       unsigned int i, j;
+       int i, j;
        unsigned const char *d = (unsigned const char *)data;
 
-       if (debug < level) return;
-       log(level, address, s, t, "%s (%d bytes):\n", title, maxsize);
-       setvbuf(log_stream, NULL, _IOFBF, 16384);
-       for (i = 0; i < maxsize; )
+       if (config->debug < level) return;
+
+       // No support for log_hex to syslog
+       if (log_stream)
        {
-               fprintf(log_stream, "%4X: ", i);
-               for (j = i; j < maxsize && j < (i + 16); j++)
-               {
-                       fprintf(log_stream, "%02X ", d[j]);
-                       if (j == i + 7)
-                               fputs(": ", log_stream);
-               }
+               log(level, address, s, t, "%s (%d bytes):\n", title, maxsize);
+               setvbuf(log_stream, NULL, _IOFBF, 16384);
 
-               for (; j < i + 16; j++)
+               for (i = 0; i < maxsize; )
                {
-                       fputs("   ", log_stream);
-                       if (j == i + 7)
-                               fputs(": ", log_stream);
-               }
+                       fprintf(log_stream, "%4X: ", i);
+                       for (j = i; j < maxsize && j < (i + 16); j++)
+                       {
+                               fprintf(log_stream, "%02X ", d[j]);
+                               if (j == i + 7)
+                                       fputs(": ", log_stream);
+                       }
 
-               fputs("  ", log_stream);
-               for (j = i; j < maxsize && j < (i + 16); j++)
-               {
-                       if (d[j] >= 0x20 && d[j] < 0x7f && d[j] != 0x20)
-                               fputc(d[j], log_stream);
-                       else
-                               fputc('.', log_stream);
+                       for (; j < i + 16; j++)
+                       {
+                               fputs("   ", log_stream);
+                               if (j == i + 7)
+                                       fputs(": ", log_stream);
+                       }
 
-                       if (j == i + 7)
-                               fputs("  ", log_stream);
+                       fputs("  ", log_stream);
+                       for (j = i; j < maxsize && j < (i + 16); j++)
+                       {
+                               if (d[j] >= 0x20 && d[j] < 0x7f && d[j] != 0x20)
+                                       fputc(d[j], log_stream);
+                               else
+                                       fputc('.', log_stream);
+
+                               if (j == i + 7)
+                                       fputs("  ", log_stream);
+                       }
+
+                       i = j;
+                       fputs("\n", log_stream);
                }
 
-               i = j;
-               fputs("\n", log_stream);
+               fflush(log_stream);
+               setbuf(log_stream, NULL);
        }
-       fflush(log_stream);
-       setbuf(log_stream, NULL);
 }
 
 
@@ -245,13 +255,13 @@ void routeset(ipt ip, ipt mask, ipt gw, u8 add)
 {
        struct rtentry r;
        memset(&r, 0, sizeof(r));
-       r.rt_dev = tapdevice;
+       r.rt_dev = config->tapdevice;
        r.rt_dst.sa_family = AF_INET;
        *(u32 *) & (((struct sockaddr_in *) &r.rt_dst)->sin_addr.s_addr) = htonl(ip);
        r.rt_gateway.sa_family = AF_INET;
        *(u32 *) & (((struct sockaddr_in *) &r.rt_gateway)->sin_addr.s_addr) = htonl(gw);
        r.rt_genmask.sa_family = AF_INET;
-       *(u32 *) & (((struct sockaddr_in *) &r.rt_genmask)->sin_addr.s_addr) = htonl(mask ? : 0xFFFFFFF);
+       *(u32 *) & (((struct sockaddr_in *) &r.rt_genmask)->sin_addr.s_addr) = htonl(mask ? mask : 0xFFFFFFF);
        r.rt_flags = (RTF_UP | RTF_STATIC);
        if (gw)
                r.rt_flags |= RTF_GATEWAY;
@@ -280,12 +290,12 @@ void inittap(void)
                log(0, 0, 0, 0, "Can't set tap interface: %s\n", strerror(errno));
                exit(-1);
        }
-       assert(strlen(ifr.ifr_name) < sizeof(tapdevice));
-       strcpy(tapdevice, ifr.ifr_name);
+       assert(strlen(ifr.ifr_name) < sizeof(config->tapdevice));
+       strncpy(config->tapdevice, ifr.ifr_name, sizeof(config->tapdevice) - 1);
        ifrfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
 
        sin.sin_family = AF_INET;
-       sin.sin_addr.s_addr = handle_interface ? bind_address : 0x01010101; // 1.1.1.1
+       sin.sin_addr.s_addr = handle_interface ? config->bind_address : 0x01010101; // 1.1.1.1
        memcpy(&ifr.ifr_addr, &sin, sizeof(struct sockaddr));
 
        if (ioctl(ifrfd, SIOCSIFADDR, (void *) &ifr) < 0)
@@ -323,12 +333,12 @@ void initudp(void)
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_port = htons(L2TPPORT);
-       addr.sin_addr.s_addr = bind_address;
+       addr.sin_addr.s_addr = config->bind_address;
        udpfd = socket(AF_INET, SOCK_DGRAM, UDP);
        setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
        if (bind(udpfd, (void *) &addr, sizeof(addr)) < 0)
        {
-               perror("bind");
+               perror("udp bind");
                exit( -1);
        }
        snoopfd = socket(AF_INET, SOCK_DGRAM, UDP);
@@ -349,7 +359,7 @@ void initudp(void)
 // Find session by IP, 0 for not found
 sessionidt sessionbyip(ipt ip)
 {
-       unsigned char *a = (char *)&ip;
+       unsigned char *a = (unsigned char *)&ip;
        char **d = (char **) ip_hash;
 
 #ifdef STAT_CALLS
@@ -365,7 +375,7 @@ sessionidt sessionbyip(ipt ip)
 
 void cache_sessionid(ipt ip, sessionidt s)
 {
-       unsigned char *a = (char *) &ip;
+       unsigned char *a = (unsigned char *) &ip;
        char **d = (char **) ip_hash;
        int i;
 
@@ -386,7 +396,7 @@ void cache_sessionid(ipt ip, sessionidt s)
 
 void uncache_sessionid(ipt ip)
 {
-       unsigned char *a = (char *) &ip;
+       unsigned char *a = (unsigned char *) &ip;
        char **d = (char **) ip_hash;
        int i;
 
@@ -399,7 +409,7 @@ void uncache_sessionid(ipt ip)
 }
 
 // Find session by username, 0 for not found
-// walled garden'd users aren't authenticated, so the username is
+// walled garden users aren't authenticated, so the username is
 // reasonably useless. Ignore them to avoid incorrect actions
 sessionidt sessionbyuser(char *username)
 {
@@ -407,7 +417,7 @@ sessionidt sessionbyuser(char *username)
 #ifdef STAT_CALLS
        STAT(call_sessionbyuser);
 #endif
-       for (s = 1; s < MAXSESSION && (session[s].walled_garden || strncmp(session[s].user, username, 128)); s++);
+       for (s = 1; s < MAXSESSION && (session[s].servicenet || strncmp(session[s].user, username, 128)); s++);
        if (s < MAXSESSION)
                return s;
        return 0;
@@ -426,7 +436,7 @@ void send_garp(ipt ip)
       exit(-1);
     }
     memset(&ifr, 0, sizeof(ifr));
-    strcpy(ifr.ifr_name, "eth0");
+    strncpy(ifr.ifr_name, "eth0", sizeof(ifr.ifr_name) - 1);
     if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0)
     {
       perror("get eth0 hwaddr");
@@ -626,7 +636,8 @@ void processipout(u8 * buf, int len)
        ip = *(u32 *)(buf + 16);
        if (!(s = sessionbyip(ip)))
        {
-//             log(4, 0, 0, 0, "IP: Can't find session for IP %s\n", inet_toa(ip));
+               log(4, 0, 0, 0, "IP: Sending ICMP host unreachable to %s\n", inet_toa(*(u32 *)(buf + 12)));
+               host_unreachable(*(u32 *)(buf + 12), *(u16 *)(buf + 4), ip, buf, (len < 64) ? 64 : len);
                return;
        }
        t = session[s].tunnel;
@@ -648,6 +659,7 @@ void processipout(u8 * buf, int len)
                u8 *p = makeppp(b, buf, len, t, s, PPPIP);
                tunnelsend(b, len + (p-b), t); // send it...
                sp->cout += len; // byte count
+               sp->total_cout += len; // byte count
                sp->pout++;
                udp_tx += len;
        }
@@ -758,7 +770,7 @@ void controladd(controlt * c, tunnelidt t, sessionidt s)
 void sessionshutdown(sessionidt s, char *reason)
 {
        int dead = session[s].die;
-       int walled_garden = session[s].walled_garden;
+       int servicenet = session[s].servicenet;
 
 #ifdef STAT_CALLS
        STAT(call_sessionshutdown);
@@ -777,17 +789,18 @@ void sessionshutdown(sessionidt s, char *reason)
        }
 
        // RADIUS Stop message
-       if (session[s].opened && !walled_garden && !dead) {
+       if (session[s].opened && !servicenet && !dead) {
                u8 r = session[s].radius;
                if (!r)
                {
-                       if (!radiusfree)
+                       if (!(r = radiusnew(s)))
                        {
                                log(1, 0, s, session[s].tunnel, "No free RADIUS sessions for Stop message\n");
                                STAT(radius_overflow);
-                       } else {
+                       }
+                       else
+                       {
                                int n;
-                               r = radiusnew(s);
                                for (n = 0; n < 15; n++)
                                        radius[r].auth[n] = rand();
                        }
@@ -812,8 +825,7 @@ void sessionshutdown(sessionidt s, char *reason)
                        }
                }
                if (session[s].throttle) throttle_session(s, 0); session[s].throttle = 0;
-               free_ip_address(session[s].ip);
-               session[s].ip = 0;
+               free_ip_address(s);
        }
        {                            // Send CDN
                controlt *c = controlnew(14); // sending CDN
@@ -851,7 +863,7 @@ void sendipcp(tunnelidt t, sessionidt s)
        *(u16 *) (q + 2) = htons(10);
        q[4] = 3;
        q[5] = 6;
-       *(u32 *) (q + 6) = htonl(myip ? : session[s].ip); // send my IP (use theirs if I dont have one)
+       *(u32 *) (q + 6) = htonl(myip ? myip : session[s].ip); // send my IP (use theirs if I dont have one)
        tunnelsend(buf, 10 + (q - buf), t); // send it
 }
 
@@ -863,7 +875,7 @@ void sessionkill(sessionidt s, char *reason)
 #endif
        sessionshutdown(s, reason);  // close radius/routes, etc.
        if (session[s].radius)
-               radius[session[s].radius].session = 0; // cant send clean accounting data, session is killed
+               radiusclear(session[s].radius, 0); // cant send clean accounting data, session is killed
        memset(&session[s], 0, sizeof(session[s]));
        session[s].next = sessionfree;
        sessionfree = s;
@@ -879,6 +891,9 @@ void tunnelkill(tunnelidt t, char *reason)
 #ifdef STAT_CALLS
        STAT(call_tunnelkill);
 #endif
+
+       tunnel[t].state = TUNNELDIE;
+
        // free control messages
        while ((c = tunnel[t].controls))
        {
@@ -889,34 +904,39 @@ void tunnelkill(tunnelidt t, char *reason)
                controlfree = c;
        }
        // kill sessions
-       for (s = 0; s < MAXSESSION; s++)
+       for (s = 1; s < MAXSESSION; s++)
                if (session[s].tunnel == t)
                        sessionkill(s, reason);
+
        // free tunnel
-       memset(&tunnel[t], 0, sizeof(tunnel[t]));
-       tunnel[t].next = tunnelfree;
+       tunnelclear(t);
        cluster_send_tunnel(t);
        log(1, 0, 0, t, "Kill tunnel %d: %s\n", t, reason);
-       tunnelfree = t;
+       tunnel[t].die = 0;
+       tunnel[t].state = TUNNELFREE;
 }
 
-// shut down a tunnel
+// shut down a tunnel cleanly
 void tunnelshutdown(tunnelidt t, char *reason)
 {
        sessionidt s;
 #ifdef STAT_CALLS
        STAT(call_tunnelshutdown);
 #endif
-       if (!tunnel[t].last || !tunnel[t].far)
-       {                          // never set up, can immediately kill
+       if (!tunnel[t].last || !tunnel[t].far || tunnel[t].state == TUNNELFREE)
+       {
+               // never set up, can immediately kill
                tunnelkill(t, reason);
                return;
        }
        log(1, 0, 0, t, "Shutting down tunnel %d (%s)\n", t, reason);
+
        // close session
-       for (s = 0; s < MAXSESSION; s++)
+       for (s = 1; s < MAXSESSION; s++)
                if (session[s].tunnel == t)
                        sessionkill(s, reason);
+
+       tunnel[t].state = TUNNELDIE;
        tunnel[t].die = now() + 700; // Clean up in 70 seconds
        cluster_send_tunnel(t);
        // TBA - should we wait for sessions to stop?
@@ -1001,7 +1021,6 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
                return;
        }
        l -= (p - buf);
-       if (t) tunnel[t].last = time_now;
        if (*buf & 0x80)
        {                          // control
                u16 message = 0xFFFF; // message type
@@ -1026,41 +1045,25 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
                // if no tunnel specified, assign one
                if (!t)
                {
-                       /*
-                       ipt ip = ntohl(*(ipt *) & addr->sin_addr);
-                       portt port = ntohs(addr->sin_port);
-
-                       // find existing tunnel that was not fully set up
-                       for (t = 0; t < MAXTUNNEL; t++)
-                       {
-                               if ((tunnel[t].ip == ip && tunnel[t].port == port) &&
-                                       (!tunnel[t].die || !tunnel[t].hostname[0]))
-                               {
-                                       char buf[600] = {0};
-                                       snprintf(buf, 600, "Duplicate tunnel with %d. ip=%u port=%d die=%d hostname=%s",
-                                                       t, tunnel[t].ip, tunnel[t].port, tunnel[t].die, tunnel[t].hostname);
-                                       tunnelshutdown(t, buf);
-                                       break;
-                               }
-                       }
-                       */
-
-                       t = tunnelfree;
-                       if (!t)
+                       if (!(t = new_tunnel()))
                        {
                                log(1, ntohl(addr->sin_addr.s_addr), 0, 0, "No more tunnels\n");
                                STAT(tunnel_overflow);
                                return;
                        }
-                       tunnelfree = tunnel[t].next;
-                       memset(&tunnel[t], 0, sizeof(tunnelt));
+                       tunnelclear(t);
                        tunnel[t].ip = ntohl(*(ipt *) & addr->sin_addr);
                        tunnel[t].port = ntohs(addr->sin_port);
                        tunnel[t].window = 4; // default window
                        log(1, ntohl(addr->sin_addr.s_addr), 0, t, "   New tunnel from %u.%u.%u.%u/%u ID %d\n", tunnel[t].ip >> 24, tunnel[t].ip >> 16 & 255, tunnel[t].ip >> 8 & 255, tunnel[t].ip & 255, tunnel[t].port, t);
                        STAT(tunnel_created);
                }
-               {                       // check sequence of this message
+
+               // This is used to time out old tunnels
+               tunnel[t].lastrec = time_now;
+
+               // check sequence of this message
+               {
                        int skip = tunnel[t].window; // track how many in-window packets are still in queue
                        if (tunnel[t].controlc)
                        {                  // some to clear maybe
@@ -1083,12 +1086,12 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
 //                             controlnull(t);
                                return;
                        }
-                       if (l)
-                               tunnel[t].nr++;   // receiver advance (do here so quoted correctly in any sends below)
-                       if (skip < 0)
-                               skip = 0;
+                       // receiver advance (do here so quoted correctly in any sends below)
+                       if (l) tunnel[t].nr++;
+                       if (skip < 0) skip = 0;
                        if (skip < tunnel[t].controlc)
-                       {                  // some control packets can now be sent that were previous stuck out of window
+                       {
+                               // some control packets can now be sent that were previous stuck out of window
                                int tosend = tunnel[t].window - skip;
                                controlt *c = tunnel[t].controls;
                                while (c && skip)
@@ -1128,7 +1131,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
                                if (flags & 0x40)
                                {
                                        // handle hidden AVPs
-                                       if (!l2tpsecret)
+                                       if (!*config->l2tpsecret)
                                        {
                                                log(1, ntohl(addr->sin_addr.s_addr), s, t, "Hidden AVP requested, but no L2TP secret.\n");
                                                fatal = flags;
@@ -1193,11 +1196,10 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
                                                }
                                                if (n > 4) {
                                                        /* %*s doesn't work?? */
-                                                       char buf[n-4+2];
-                                                       memcpy(buf, b+4, n-4);
-                                                       buf[n-4+1] = '\0';
+                                                       char *buf = (char *)strndup(b+4, n-4);
                                                        log(4, ntohl(addr->sin_addr.s_addr), s, t, "   Error String: %s\n",
                                                                buf);
+                                                       free(buf);
                                                }
                                                break;
                                        }
@@ -1222,8 +1224,7 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
 //                                     log(4, ntohl(addr->sin_addr.s_addr), s, t, "Bearer capabilities\n");
                                        break;
                                case 5:         // tie breaker
-                                       // We never open tunnels, so we don't
-                                       // care about tie breakers
+                                       // We never open tunnels, so we don't care about tie breakers
 //                                     log(4, ntohl(addr->sin_addr.s_addr), s, t, "Tie breaker\n");
                                        continue;
                                case 6:     // firmware revision
@@ -1422,11 +1423,13 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
                                                control16(c, 9, t, 1); // assigned tunnel
                                                controladd(c, t, s); // send the resply
                                        }
+                                       tunnel[t].state = TUNNELOPENING;
                                        break;
                                case 2:       // SCCRP
-                                       // TBA
+                                       tunnel[t].state = TUNNELOPEN;
                                        break;
                                case 3:       // SCCN
+                                       tunnel[t].state = TUNNELOPEN;
                                        controlnull(t); // ack
                                        break;
                                case 4:       // StopCCN
@@ -1457,39 +1460,36 @@ void processudp(u8 * buf, int len, struct sockaddr_in *addr)
                                                u8 r;
                                                controlt *c;
 
+                                               s = sessionfree;
+                                               sessionfree = session[s].next;
+                                               memset(&session[s], 0, sizeof(session[s]));
+
                                                // make a RADIUS session
-                                               if (!radiusfree)
+                                               if (!(r = radiusnew(s)))
                                                {
-                                                       STAT(radius_overflow);
                                                        log(1, ntohl(addr->sin_addr.s_addr), s, t, "No free RADIUS sessions for ICRQ\n");
+                                                       sessionkill(s, "no free RADIUS sesions");
                                                        return;
                                                }
 
                                                c = controlnew(11); // sending ICRP
-                                               s = sessionfree;
-                                               sessionfree = session[s].next;
-                                               memset(&session[s], 0, sizeof(session[s]));
                                                session[s].id = sessionid++;
                                                session[s].opened = time(NULL);
                                                session[s].tunnel = t;
                                                session[s].far = asession;
+                                               session[s].last_packet = time_now;
                                                log(3, ntohl(addr->sin_addr.s_addr), s, t, "New session (%d/%d)\n", tunnel[t].far, session[s].far);
                                                control16(c, 14, s, 1); // assigned session
                                                controladd(c, t, s); // send the reply
-                                               r = radiusfree;
-                                               radiusfree = radius[r].next;
-                                               memset(&radius[r], 0, sizeof(radius[r]));
-                                               session[s].radius = r;
-                                               radius[r].session = s;
                                                {
                                                        // Generate a random challenge
                                                        int n;
                                                        for (n = 0; n < 15; n++)
                                                                radius[r].auth[n] = rand();
                                                }
-                                               strcpy(radius[r].calling, calling);
-                                               strcpy(session[s].called, called);
-                                               strcpy(session[s].calling, calling);
+                                               strncpy(radius[r].calling, calling, sizeof(radius[r].calling) - 1);
+                                               strncpy(session[s].called, called, sizeof(session[s].called) - 1);
+                                               strncpy(session[s].calling, calling, sizeof(session[s].calling) - 1);
                                                STAT(session_created);
                                        }
                                        break;
@@ -1645,23 +1645,26 @@ void mainloop(void)
        FD_SET(tapfd, &cr);
        FD_SET(radfd, &cr);
        FD_SET(controlfd, &cr);
-#ifdef HAVE_LIBCLI
        FD_SET(clifd, &cr);
-#endif
        if (cluster_sockfd) FD_SET(cluster_sockfd, &cr);
        cn = udpfd;
        if (cn < radfd) cn = radfd;
        if (cn < tapfd) cn = tapfd;
        if (cn < controlfd) cn = controlfd;
-#ifdef HAVE_LIBCLI
        if (cn < clifd) cn = clifd;
-#endif
        if (cn < cluster_sockfd) cn = cluster_sockfd;
 
        while (!main_quit)
        {
                fd_set r;
                int n = cn;
+
+               if (config->reload_config)
+               {
+                       // Update the config state based on config settings
+                       update_config();
+               }
+
                memcpy(&r, &cr, sizeof(fd_set));
                n = select(n + 1, &r, 0, 0, &to);
                if (n < 0)
@@ -1686,7 +1689,6 @@ void mainloop(void)
                                processcluster(buf, recvfrom(cluster_sockfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen));
                        else if (FD_ISSET(controlfd, &r))
                                processcontrol(buf, recvfrom(controlfd, buf, sizeof(buf), MSG_WAITALL, (void *) &addr, &alen), &addr);
-#ifdef HAVE_LIBCLI
                        else if (FD_ISSET(clifd, &r))
                        {
                                struct sockaddr_in addr;
@@ -1704,7 +1706,6 @@ void mainloop(void)
                                        close(sockfd);
                                }
                        }
-#endif
                        else
                        {
                                log(1, 0, 0, 0, "Main select() loop returned %d, but no fds have data waiting\n", n);
@@ -1717,7 +1718,8 @@ void mainloop(void)
                        sessionidt s;
                        tunnelidt t;
                        u8 r;
-                       for (r = 0; r < MAXRADIUS; r++)
+
+                       for (r = 1; r < MAXRADIUS; r++)
                                if (radius[r].state && radius[r].retry)
                                {
                                        if (radius[r].retry <= when)
@@ -1725,7 +1727,7 @@ void mainloop(void)
                                        if (radius[r].retry && radius[r].retry < best)
                                                best = radius[r].retry;
                                }
-                       for (t = 0; t < MAXTUNNEL; t++)
+                       for (t = 1; t < MAXTUNNEL; t++)
                        {
                                // check for expired tunnels
                                if (tunnel[t].die && tunnel[t].die <= when)
@@ -1756,7 +1758,7 @@ void mainloop(void)
                                                best = tunnel[t].retry;
                                }
                                // Send hello
-                               if (tunnel[t].ip && !tunnel[t].die && tunnel[t].last < when + 600 && !tunnel[t].controlc)
+                               if (tunnel[t].state == TUNNELOPEN && tunnel[t].lastrec < when + 600)
                                {
                                        controlt *c = controlnew(6); // sending HELLO
                                        controladd(c, t, 0); // send the message
@@ -1764,15 +1766,14 @@ void mainloop(void)
                                }
                        }
 
-#ifdef HAVE_LIBCLI
                        // Check for sessions that have been killed from the CLI
                        if (cli_session_kill[0])
                        {
                                int i;
-                               for (i = 0; i < MAXSESSION && cli_session_kill[i]; i++)
+                               for (i = 1; i < MAXSESSION && cli_session_kill[i]; i++)
                                {
                                        log(2, 0, cli_session_kill[i], 0, "Dropping session by CLI\n");
-                                       sessionshutdown(cli_session_kill[i], "Requested by CLI");
+                                       sessionshutdown(cli_session_kill[i], "Requested by administrator");
                                        cli_session_kill[i] = 0;
                                }
                        }
@@ -1780,29 +1781,28 @@ void mainloop(void)
                        if (cli_tunnel_kill[0])
                        {
                                int i;
-                               for (i = 0; i < MAXTUNNEL && cli_tunnel_kill[i]; i++)
+                               for (i = 1; i < MAXTUNNEL && cli_tunnel_kill[i]; i++)
                                {
                                        log(2, 0, cli_tunnel_kill[i], 0, "Dropping tunnel by CLI\n");
-                                       tunnelshutdown(cli_tunnel_kill[i], "Requested by CLI");
+                                       tunnelshutdown(cli_tunnel_kill[i], "Requested by administrator");
                                        cli_tunnel_kill[i] = 0;
                                }
                        }
-#endif
 
-                       for (s = 0; s < MAXSESSION; s++)
+                       for (s = 1; s < MAXSESSION; s++)
                        {
                                // check for expired sessions
                                if (session[s].die && session[s].die <= when)
                                {
-                                       STAT(session_timeout);
                                        sessionkill(s, "Expired");
                                        continue;
                                }
 
                                // Drop sessions who have not responded within IDLE_TIMEOUT seconds
-                               if (session[s].user[0] && (time_now - session[s].last_packet >= IDLE_TIMEOUT))
+                               if (session[s].last_packet && (time_now - session[s].last_packet >= IDLE_TIMEOUT))
                                {
                                        sessionkill(s, "No response to LCP ECHO requests");
+                                       STAT(session_timeout);
                                        continue;
                                }
 
@@ -1823,7 +1823,7 @@ void mainloop(void)
                                        continue;
                                }
                        }
-                       if (accounting_dir && next_acct <= when)
+                       if (config->accounting_dir && next_acct <= when)
                        {
                                // Dump accounting data
                                next_acct = when + ACCT_TIME;
@@ -1834,7 +1834,7 @@ void mainloop(void)
                        {
                                // Dump accounting data
                                next_cluster_ping = when + 50;
-                               cluster_send_message(cluster_address, bind_address, C_PING, hostname, strlen(hostname));
+                               cluster_send_message(config->cluster_address, config->bind_address, C_PING, hostname, strlen(hostname));
                        }
 
                        if (best <= when)
@@ -1852,38 +1852,47 @@ void initdata(void)
        int i;
 
        _statistics = mmap(NULL, sizeof(struct Tstats), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
-       if (_statistics <= 0)
+       if (_statistics == MAP_FAILED)
        {
                log(0, 0, 0, 0, "Error doing mmap for _statistics: %s\n", strerror(errno));
                exit(1);
        }
+       config = mmap(NULL, sizeof(struct configt), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
+       if (config == MAP_FAILED)
+       {
+               log(0, 0, 0, 0, "Error doing mmap for configuration: %s\n", strerror(errno));
+               exit(1);
+       }
+       memset(config, 0, sizeof(struct configt));
+       time(&config->start_time);
+       strncpy(config->config_file, CONFIGFILE, sizeof(config->config_file) - 1);
        tunnel = mmap(NULL, sizeof(tunnelt) * MAXTUNNEL, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
-       if (tunnel <= 0)
+       if (tunnel == MAP_FAILED)
        {
                log(0, 0, 0, 0, "Error doing mmap for tunnels: %s\n", strerror(errno));
                exit(1);
        }
        session = mmap(NULL, sizeof(sessiont) * MAXSESSION, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
-       if (session <= 0)
+       if (session == MAP_FAILED)
        {
                log(0, 0, 0, 0, "Error doing mmap for sessions: %s\n", strerror(errno));
                exit(1);
        }
        radius = mmap(NULL, sizeof(radiust) * MAXRADIUS, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
-       if (radius <= 0)
+       if (radius == MAP_FAILED)
        {
                log(0, 0, 0, 0, "Error doing mmap for radius: %s\n", strerror(errno));
                exit(1);
        }
        ip_address_pool = mmap(NULL, sizeof(ippoolt) * MAXIPPOOL, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
-       if (ip_address_pool <= 0)
+       if (ip_address_pool == MAP_FAILED)
        {
                log(0, 0, 0, 0, "Error doing mmap for radius: %s\n", strerror(errno));
                exit(1);
        }
 #ifdef RINGBUFFER
        ringbuffer = mmap(NULL, sizeof(struct Tringbuffer), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
-       if (ringbuffer <= 0)
+       if (ringbuffer == MAP_FAILED)
        {
                log(0, 0, 0, 0, "Error doing mmap for radius: %s\n", strerror(errno));
                exit(1);
@@ -1891,16 +1900,15 @@ void initdata(void)
        memset(ringbuffer, 0, sizeof(struct Tringbuffer));
 #endif
 
-#ifdef HAVE_LIBCLI
        cli_session_kill = mmap(NULL, sizeof(sessionidt) * MAXSESSION, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
-       if (cli_session_kill <= 0)
+       if (cli_session_kill == MAP_FAILED)
        {
                log(0, 0, 0, 0, "Error doing mmap for cli session kill: %s\n", strerror(errno));
                exit(1);
        }
        memset(cli_session_kill, 0, sizeof(sessionidt) * MAXSESSION);
        cli_tunnel_kill = mmap(NULL, sizeof(tunnelidt) * MAXSESSION, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
-       if (cli_tunnel_kill <= 0)
+       if (cli_tunnel_kill == MAP_FAILED)
        {
                log(0, 0, 0, 0, "Error doing mmap for cli tunnel kill: %s\n", strerror(errno));
                exit(1);
@@ -1908,33 +1916,24 @@ void initdata(void)
        memset(cli_tunnel_kill, 0, sizeof(tunnelidt) * MAXSESSION);
 
        filter_buckets = mmap(NULL, sizeof(tbft) * MAXSESSION, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);
-       if (filter_buckets <= 0)
+       if (filter_buckets == MAP_FAILED)
        {
                log(0, 0, 0, 0, "Error doing mmap for filter buckets: %s\n", strerror(errno));
                exit(1);
        }
        memset(filter_buckets, 0, sizeof(tbft) * MAXSESSION);
 
-#endif
-
        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);
-       for (i = 1; i < MAXTUNNEL - 1; i++)
-               tunnel[i].next = i + 1;
-       tunnel[MAXTUNNEL - 1].next = 0;
-       tunnelfree = 1;
        for (i = 1; i < MAXSESSION - 1; i++)
                session[i].next = i + 1;
        session[MAXSESSION - 1].next = 0;
        sessionfree = 1;
-       for (i = 1; i < MAXRADIUS - 1; i++)
-               radius[i].next = i + 1;
-       radius[MAXRADIUS - 1].next = 0;
-       radiusfree = 1;
        if (!*hostname)
        {
+               char *p;
                // Grab my hostname unless it's been specified
                gethostname(hostname, sizeof(hostname));
                {
@@ -1942,12 +1941,10 @@ void initdata(void)
                        if (h)
                                myip = ntohl(*(u32 *) h->h_addr);
                }
+
+               if ((p = strstr(hostname, ".optusnet.com.au"))) *p = 0;
        }
        _statistics->start_time = _statistics->last_reset = time(NULL);
-
-       // Start the timer routine off
-       time(&time_now);
-       strftime(time_now_string, 64, "%Y-%m-%d %H:%M:%S", localtime(&time_now));
 }
 
 void initiptables(void)
@@ -1957,51 +1954,78 @@ void initiptables(void)
        system("iptables -t mangle -F l2tpns");
 }
 
-ipt assign_ip_address()
+int assign_ip_address(sessionidt s)
 {
-       int c = 0;
+       unsigned i;
+       int best = -1;
+       clockt best_time = time_now;
+       char *u = session[s].user;
+       char reuse = 0;
+
 #ifdef STAT_CALLS
        STAT(call_assign_ip_address);
 #endif
-       ip_pool_index++;
-       while (1)
+       for (i = 0; i < ip_pool_size; i++)
        {
-               if (ip_pool_index >= ip_pool_size)
+               if (!ip_address_pool[i].address || ip_address_pool[i].assigned)
+                       continue;
+
+               if (!session[s].servicenet && ip_address_pool[i].user[0] && !strcmp(u, ip_address_pool[i].user))
                {
-                       if (++c == 2)
-                               return 0;
-                       ip_pool_index = 0;
+                       best = i;
+                       reuse = 1;
+                       break;
                }
-               if (!ip_address_pool[ip_pool_index].assigned && ip_address_pool[ip_pool_index].address)
+
+               if (ip_address_pool[i].last < best_time)
                {
-                       ip_address_pool[ip_pool_index].assigned = 1;
-                       log(4, ip_address_pool[ip_pool_index].address, 0, 0, "assign_ip_address(): Allocating ip address %lu from pool\n", ip_pool_index);
-                       STAT(ip_allocated);
-                       return ntohl(ip_address_pool[ip_pool_index].address);
+                       best = i;
+                       if (!(best_time = ip_address_pool[i].last))
+                               break; // never used, grab this one
                }
-               ip_pool_index++;
        }
-       return 0;
+
+       if (best < 0)
+       {
+               log(0, 0, s, session[s].tunnel, "assign_ip_address(): out of addresses\n");
+               return 0;
+       }
+
+       session[s].ip = ntohl(ip_address_pool[best].address);
+       session[s].ip_pool_index = best;
+       ip_address_pool[best].assigned = 1;
+       ip_address_pool[best].last = time_now;
+       if (session[s].servicenet)
+               /* 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, ip_address_pool[best].address, s, session[s].tunnel,
+               "assign_ip_address(): %s ip address %lu from pool\n", reuse ? "Reusing" : "Allocating", best);
+
+       return 1;
 }
 
-void free_ip_address(ipt address)
+void free_ip_address(sessionidt s)
 {
-       int i;
-       ipt a;
+       int i = session[s].ip_pool_index;
+
 #ifdef STAT_CALLS
        STAT(call_free_ip_address);
 #endif
 
-       a = ntohl(address);
-       for (i = 0; i <= ip_pool_size; i++)
-       {
-               if (ip_address_pool[i].address == a)
-               {
-                       STAT(ip_freed);
-                       ip_address_pool[i].assigned = 0;
-               }
-       }
-       uncache_sessionid(htonl(address));
+       if (!session[s].ip)
+               return; // what the?
+
+       STAT(ip_freed);
+       uncache_sessionid(session[s].ip);
+       session[s].ip = 0;
+       ip_address_pool[i].assigned = 0;
+       ip_address_pool[i].last = time_now;
 }
 
 // Initialize the IP address pool
@@ -2037,7 +2061,7 @@ void initippool()
                                exit(-1);
                        }
                        // This entry is for a specific IP only
-                       if (src != bind_address)
+                       if (src != config->bind_address)
                                continue;
                        *p = ':';
                        pool = p+1;
@@ -2070,7 +2094,7 @@ void initippool()
                        // Add a static route for this pool
                        log(5, 0, 0, 0, "Adding route for address pool %s/%d\n", inet_toa(htonl(start)), 32+mask);
                        memset(&r, 0, sizeof(r));
-                       r.rt_dev = tapdevice;
+                       r.rt_dev = config->tapdevice;
                        r.rt_dst.sa_family = AF_INET;
                        *(u32 *) & (((struct sockaddr_in *) &r.rt_dst)->sin_addr.s_addr) = htonl(start);
                        r.rt_genmask.sa_family = AF_INET;
@@ -2099,6 +2123,7 @@ void snoop_send_packet(char *packet, u16 size)
        if (!snoop_addr.sin_port || snoopfd <= 0 || size <= 0 || !packet)
                return;
 
+       log(5, 0, 0, 0, "Snooping packet at %p (%d bytes) to %s:%d\n", packet, size, inet_toa(snoop_addr.sin_addr.s_addr), 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, 0, "Error sending intercept packet: %s\n", strerror(errno));
        STAT(packets_snooped);
@@ -2116,11 +2141,11 @@ void dump_acct_info()
        STAT(call_dump_acct_info);
 #endif
     strftime(timestr, 64, "%Y%m%d%H%M%S", localtime(&t));
-    snprintf(filename, 1024, "%s/%s", accounting_dir, timestr);
+    snprintf(filename, 1024, "%s/%s", config->accounting_dir, timestr);
 
     for (i = 0; i < MAXSESSION; i++)
     {
-       if (!session[i].opened || !session[i].cin || !session[i].cout || !*session[i].user || session[i].walled_garden)
+       if (!session[i].opened || !session[i].cin || !session[i].cout || !*session[i].user || session[i].servicenet)
                continue;
        if (!f)
        {
@@ -2176,21 +2201,18 @@ int main(int argc, char *argv[])
        time(&basetime);             // start clock
        // scan args
 
-       while ((o = getopt(argc, argv, "vc:f:h:a:")) >= 0)
+       while ((o = getopt(argc, argv, "vc:h:a:")) >= 0)
        {
                switch (o)
                {
                        case 'v':
-                               debug++;
+                               config->debug++;
                                break;
                        case 'c':
-                               config_file = strdup(optarg);
-                               break;
-                       case 'f':
-                               log_filename = strdup(optarg);
+                               strncpy(config->config_file, optarg, sizeof(config->config_file) - 1);
                                break;
                        case 'h':
-                               strncpy(hostname, optarg, 1000);
+                               strncpy(hostname, optarg, 999);
                                break;
                        case 'a':
                                myip = inet_addr(optarg);
@@ -2198,45 +2220,51 @@ int main(int argc, char *argv[])
                                        log(0, 0, 0, 0, "Invalid ip %s\n", optarg);
                                        exit(-1);
                                }
-                               bind_address = myip;
+                               config->bind_address = myip;
                                handle_interface = 1;
                                break;
                        case '?':
                        default:
-                               printf("Args are:\n\t-c <file>\tConfig file\n\t-h <hostname>\tForce hostname\n\t-a <address>\tUse specific address\n\t-f <file>\tLog File\n\t-v\t\tDebug\n");
+                               printf("Args are:\n\t-c <file>\tConfig file\n\t-h <hostname>\tForce hostname\n\t-a <address>\tUse specific address\n\t-v\t\tDebug\n");
                                return (0);
                                break;
                }
        }
 
+       // Start the timer routine off
+       time(&time_now);
+       strftime(time_now_string, 64, "%Y-%m-%d %H:%M:%S", localtime(&time_now));
+
        initiptables();
        initplugins();
-       read_config_file();
        initdata();
-       log(0, 0, 0, 0, "$Id: l2tpns.c,v 1.1 2003/12/16 07:07:39 fred_nerk Exp $\n(c) Copyright 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n");
+       init_cli();
+       read_config_file();
+       log(0, 0, 0, 0, "$Id: l2tpns.c,v 1.2 2004/03/05 00:09:03 fred_nerk Exp $\n(c) Copyright 2002 FireBrick (Andrews & Arnold Ltd / Watchfront Ltd) - GPL licenced\n");
 
        /* Start up the cluster first, so that we don't have two machines with
         * the same IP at once.
         * This is still racy, but the second GARP should fix that
         */
-       cluster_init(bind_address, 0);
-       cluster_send_message(cluster_address, bind_address, C_HELLO, hostname, strlen(hostname));
+       cluster_init(config->bind_address, 0);
+       cluster_send_message(config->cluster_address, config->bind_address, C_HELLO, hostname, strlen(hostname));
 
        inittap();
-       log(1, 0, 0, 0, "Set up on interface %s\n", tapdevice);
+       log(1, 0, 0, 0, "Set up on interface %s\n", config->tapdevice);
 
        initudp();
        initrad();
        initippool();
        init_rl();
        if (handle_interface) {
-               send_garp(bind_address);
+               send_garp(config->bind_address);
        }
-       read_state();
 
-#ifdef HAVE_LIBCLI
-       init_cli();
-#endif
+       // If NOSTATEFILE exists, we will ignore any updates from the cluster master for this execution
+       if (!unlink(NOSTATEFILE))
+           config->ignore_cluster_updates = 1;
+
+       read_state();
 
        signal(SIGALRM, sigalrm_handler);
        signal(SIGHUP, sighup_handler);
@@ -2245,55 +2273,47 @@ int main(int argc, char *argv[])
        signal(SIGQUIT, sigquit_handler);
        signal(SIGCHLD, sigchild_handler);
        signal(SIGSEGV, sigsegv_handler);
-       if (debug)
-       {
-               int n;
-               for (n = 0; n < numradiusservers; n++)
-                       log(1, 0, 0, 0, "RADIUS to %s\n", inet_toa(htonl(radiusserver[n])));
-       }
 
        alarm(1);
 
        // Drop privileges here
-       if (target_uid > 0 && geteuid() == 0)
-               setuid(target_uid);
+       if (config->target_uid > 0 && geteuid() == 0)
+               setuid(config->target_uid);
 
        mainloop();
-
-       if (l2tpsecret) free(l2tpsecret);
-       if (log_filename) free(log_filename);
-       if (snoop_destination_host) free(snoop_destination_host);
-       if (radiussecret) free(radiussecret);
-
        return 0;
 }
 
 void sighup_handler(int junk)
 {
-       if (log_stream != stderr)
+       if (log_stream && log_stream != stderr)
+       {
                fclose(log_stream);
+               log_stream = NULL;
+       }
 
-       log_stream = NULL;
        read_config_file();
 }
 
 void sigalrm_handler(int junk)
 {
        // Log current traffic stats
-       if (dump_speed)
-       {
-               printf("UDP-ETH:%1.0f/%1.0f  ETH-UDP:%1.0f/%1.0f  TOTAL:%0.1f   IN:%lu OUT:%lu\n",
-                       (udp_rx / 1024.0 / 1024.0 * 8),
-                       (eth_tx / 1024.0 / 1024.0 * 8),
-                       (eth_rx / 1024.0 / 1024.0 * 8),
-                       (udp_tx / 1024.0 / 1024.0 * 8),
-                       ((udp_tx + udp_rx + eth_tx + eth_rx) / 1024.0 / 1024.0 * 8),
-                       udp_rx_pkt, eth_rx_pkt);
 
-               udp_tx = udp_rx = 0;
-               udp_rx_pkt = eth_rx_pkt = 0;
-               eth_tx = eth_rx = 0;
-       }
+       snprintf(config->bandwidth, sizeof(config->bandwidth),
+               "UDP-ETH:%1.0f/%1.0f  ETH-UDP:%1.0f/%1.0f  TOTAL:%0.1f   IN:%lu OUT:%lu",
+               (udp_rx / 1024.0 / 1024.0 * 8),
+               (eth_tx / 1024.0 / 1024.0 * 8),
+               (eth_rx / 1024.0 / 1024.0 * 8),
+               (udp_tx / 1024.0 / 1024.0 * 8),
+               ((udp_tx + udp_rx + eth_tx + eth_rx) / 1024.0 / 1024.0 * 8),
+               udp_rx_pkt, eth_rx_pkt);
+
+       udp_tx = udp_rx = 0;
+       udp_rx_pkt = eth_rx_pkt = 0;
+       eth_tx = eth_rx = 0;
+
+       if (config->dump_speed)
+               printf("%s\n", config->bandwidth);
 
        // Update the internal time counter
        time(&time_now);
@@ -2311,48 +2331,43 @@ void sigalrm_handler(int junk)
 void sigterm_handler(int junk)
 {
        log(1, 0, 0, 0, "Shutting down cleanly\n");
-       if (config_save_state)
+       if (config->save_state)
                dump_state();
        main_quit++;
 }
 
 void sigquit_handler(int junk)
 {
+       FILE *f;
        int i;
+
        log(1, 0, 0, 0, "Shutting down without saving sessions\n");
-       for (i = 0; i < MAXSESSION; i++)
+       for (i = 1; i < MAXSESSION; i++)
        {
                if (session[i].opened)
                        sessionkill(i, "L2TPNS Closing");
        }
-       for (i = 0; i < MAXTUNNEL; i++)
+       for (i = 1; i < MAXTUNNEL; i++)
        {
-               if (tunnel[i].ip)
+               if (tunnel[i].ip || tunnel[i].state)
                        tunnelshutdown(i, "L2TPNS Closing");
        }
+
+       cluster_send_goodbye();
+
+       // Touch a file which says not to reload the state
+       f = fopen(NOSTATEFILE, "w");
+       if (f) fclose(f);
+
        main_quit++;
 }
 
 void sigchild_handler(int signal)
 {
-       int status;
-       int pid;
-
-       pid = wait(&status);
-#ifdef HAVE_LIBCLI
-       status = (WIFEXITED(status)) ? WEXITSTATUS(status) : 0;
-       if (pid == cli_pid)
-       {
-               if (status == 0)
-                       log(3, 0, 0, 0, "CLI client closed connection\n");
-               else
-                       log(2, 0, 0, 0, "CLI child died with rc %d!\n", status);
-       }
-#endif
+       while (waitpid(-1, NULL, WNOHANG) > 0)
+           ;
 }
 
-void *backtrace_buffer[30] = {0};
-
 void sigsegv_handler(int signal)
 {
        log(0, 0, 0, 0, "----------------------------------------------\n");
@@ -2363,135 +2378,169 @@ void sigsegv_handler(int signal)
 
 void read_state()
 {
-       struct stat sb;
-       FILE *f;
+    struct stat sb;
+    int i;
+    ippoolt itmp;
+    FILE *f;
+    char magic[sizeof(DUMP_MAGIC)-1];
+    u32 buf[2];
 
-       if (!config_save_state) return;
+    if (!config->save_state)
+       return;
 
-       if (stat(STATEFILE, &sb) < 0)
-               return;
+    // Ignore saved state if NOSTATEFILE exists
+    if (config->ignore_cluster_updates)
+    {
+       unlink(STATEFILE);
+       return;
+    }
 
-       if (sb.st_mtime < (time(NULL) - 60))
-       {
-               log(0, 0, 0, 0, "State file is too old to read\n");
-               unlink(STATEFILE);
-               return;
-       }
+    if (stat(STATEFILE, &sb) < 0)
+       return;
 
-       if (!(f = fopen(STATEFILE, "r")))
+    if (sb.st_mtime < (time(NULL) - 60))
+    {
+       log(0, 0, 0, 0, "State file is too old to read, ignoring\n");
+       unlink(STATEFILE);
+       return;
+    }
+
+    f = fopen(STATEFILE, "r");
+    unlink(STATEFILE);
+
+    if (!f)
+    {
+       log(0, 0, 0, 0, "Can't read state file: %s\n", strerror(errno));
+       exit(1);
+    }
+
+    if (fread(magic, sizeof(magic), 1, f) != 1 || strncmp(magic, DUMP_MAGIC, sizeof(magic)))
+    {
+       log(0, 0, 0, 0, "Bad state file magic\n");
+       exit(1);
+    }
+
+    log(1, 0, 0, 0, "Reading state information\n");
+    if (fread(buf, sizeof(buf), 1, f) != 1 || buf[0] > MAXIPPOOL || buf[1] != sizeof(ippoolt))
+    {
+       log(0, 0, 0, 0, "Error/mismatch reading ip pool header from state file\n");
+       exit(1);
+    }
+
+    if (buf[0] > ip_pool_size)
+    {
+       log(0, 0, 0, 0, "ip pool has shrunk!  state = %d, current = %d\n", buf[0], ip_pool_size);
+       exit(1);
+    }
+
+    log(2, 0, 0, 0, "Loading %u ip addresses\n", buf[0]);
+    for (i = 0; i < buf[0]; i++)
+    {
+       if (fread(&itmp, sizeof(itmp), 1, f) != 1)
        {
-               log(0, 0, 0, 0, "Can't read state file: %s\n", strerror(errno));
-               unlink(STATEFILE);
-               return;
+           log(0, 0, 0, 0, "Error reading ip %d from state file: %s\n", i, strerror(errno));
+           exit(1);
        }
-       fseek(f, 0, 0);
 
-       log(1, 0, 0, 0, "Reading state information\n");
+       if (itmp.address != ip_address_pool[i].address)
        {
-               u32 i, numtunnels;
-               if (fread(&numtunnels, sizeof(numtunnels), 1, f) <= 0)
-               {
-                       log(0, 0, 0, 0, "Error reading saved state (tunnel count): %s\n", strerror(errno));
-                       fclose(f);
-                       unlink(STATEFILE);
-                       return;
-               }
-               log(2, 0, 0, 0, "Reading %lu tunnels\n", numtunnels);
-               fread(tunnel, sizeof(tunnelt), numtunnels, f);
-               tunnelfree = 0;
-               for (i = 0; i < numtunnels; i++)
-               {
-                       tunnel[i].controlc = 0;
-                       tunnel[i].controls = NULL;
-                       tunnel[i].controle = NULL;
-                       if (*tunnel[i].hostname)
-                       {
-                               log(3, 0, 0, 0, "Created tunnel for %s\n", tunnel[i].hostname);
-                               tunnelfree = i;
-                       }
-               }
-               tunnelfree++;
+           log(0, 0, 0, 0, "Mismatched ip %d from state file: pool may only be extended\n", i);
+           exit(1);
        }
+
+       memcpy(&ip_address_pool[i], &itmp, sizeof(itmp));
+    }
+
+    if (fread(buf, sizeof(buf), 1, f) != 1 || buf[0] != MAXTUNNEL || buf[1] != sizeof(tunnelt))
+    {
+       log(0, 0, 0, 0, "Error/mismatch reading tunnel header from state file\n");
+       exit(1);
+    }
+
+    log(2, 0, 0, 0, "Loading %u tunnels\n", MAXTUNNEL);
+    if (fread(tunnel, sizeof(tunnelt), MAXTUNNEL, f) != MAXTUNNEL)
+    {
+       log(0, 0, 0, 0, "Error reading tunnel data from state file\n");
+       exit(1);
+    }
+
+    for (i = 0; i < MAXTUNNEL; i++)
+    {
+       tunnel[i].controlc = 0;
+       tunnel[i].controls = NULL;
+       tunnel[i].controle = NULL;
+       if (*tunnel[i].hostname)
+           log(3, 0, 0, 0, "Created tunnel for %s\n", tunnel[i].hostname);
+    }
+
+    if (fread(buf, sizeof(buf), 1, f) != 1 || buf[0] != MAXSESSION || buf[1] != sizeof(sessiont))
+    {
+       log(0, 0, 0, 0, "Error/mismatch reading session header from state file\n");
+       exit(1);
+    }
+
+    log(2, 0, 0, 0, "Loading %u sessions\n", MAXSESSION);
+    if (fread(session, sizeof(sessiont), MAXSESSION, f) != MAXSESSION)
+    {
+       log(0, 0, 0, 0, "Error reading session data from state file\n");
+       exit(1);
+    }
+
+    for (i = 0; i < MAXSESSION; i++)
+    {
+       session[i].tbf = 0;
+       session[i].throttle = 0;
+       if (session[i].opened)
        {
-               u32 i, numsessions;
-               if (fread(&numsessions, sizeof(numsessions), 1, f) <= 0)
-               {
-                       log(0, 0, 0, 0, "Error reading saved state (session count): %s\n", strerror(errno));
-                       fclose(f);
-                       unlink(STATEFILE);
-                       return;
-               }
-               log(2, 0, 0, 0, "Reading %lu sessions\n", numsessions);
-               if (fread(session, sizeof(sessiont), numsessions, f) < numsessions)
-               {
-                       log(0, 0, 0, 0, "Error reading saved state (%d sessions): %s\n", numsessions, strerror(errno));
-                       fclose(f);
-                       unlink(STATEFILE);
-                       return;
-               }
-               for (i = 0; i < numsessions; i++)
-               {
-                       session[i].tbf = 0;
-                       session[i].throttle = 0;
-                       if (session[i].opened)
-                       {
-                               log(2, 0, i, 0, "Loaded active session for user %s\n", session[i].user);
-                               if (session[i].ip && session[i].ip != 0xFFFFFFFE)
-                               {
-                                       int x;
-                                       sessionsetup(session[i].tunnel, i, 0);
-                                       for (x = 0; x < MAXIPPOOL && ip_address_pool[x].address; x++)
-                                       {
-                                               if (ip_address_pool[x].address == session[i].ip)
-                                               {
-                                                       ip_address_pool[x].assigned = 1;
-                                                       break;
-                                               }
-                                       }
-                               }
-                               else
-                               {
-                                       log(2, 0, i, 0, "No IP for session\n");
-                               }
-                       }
-               }
-               for (i = 0; i < numsessions && session[i].opened; i++)
-                       sessionfree = session[i].next;
+           log(2, 0, i, 0, "Loaded active session for user %s\n", session[i].user);
+           if (session[i].ip && session[i].ip != 0xFFFFFFFE)
+               sessionsetup(session[i].tunnel, i, 0);
        }
-       fclose(f);
-       log(0, 0, 0, 0, "Loaded saved state information\n");
-       unlink(STATEFILE);
+    }
+
+    fclose(f);
+    log(0, 0, 0, 0, "Loaded saved state information\n");
 }
 
 void dump_state()
 {
-       FILE *f;
+    FILE *f;
+    u32 buf[2];
 
-       if (!config_save_state) return;
+    if (!config->save_state)
+       return;
 
-       if ((f = fopen(STATEFILE, "w")))
-       {
-               u32 i;
-               log(1, 0, 0, 0, "Dumping state information\n");
+    do {
+       if (!(f = fopen(STATEFILE, "w")))
+           break;
 
-               i = MAXTUNNEL;
-               fwrite(&i, sizeof(i), 1, f);    // Number of tunnels
+       log(1, 0, 0, 0, "Dumping state information\n");
 
-               log(2, 0, 0, 0, "Dumping %lu tunnels\n", i);
-               fwrite(tunnel, sizeof(tunnelt), MAXTUNNEL, f);
+       if (fwrite(DUMP_MAGIC, sizeof(DUMP_MAGIC)-1, 1, f) != 1) break;
 
-               i = MAXSESSION;
-               fwrite(&i, sizeof(i), 1, f);    // Number of sessions
-               log(2, 0, 0, 0, "Dumping %lu sessions\n", i);
-               fwrite(session, sizeof(sessiont), MAXSESSION, f);
+       log(2, 0, 0, 0, "Dumping %u ip addresses\n", ip_pool_size);
+       buf[0] = ip_pool_size;
+       buf[1] = sizeof(ippoolt);
+       if (fwrite(buf, sizeof(buf), 1, f) != 1) break;
+       if (fwrite(ip_address_pool, sizeof(ippoolt), ip_pool_size, f) != ip_pool_size) break;
 
-               fclose(f);
-       }
-       else
-       {
-               log(0, 0, 0, 0, "Can't write state information: %s\n", strerror(errno));
-       }
-       return;
+       log(2, 0, 0, 0, "Dumping %u tunnels\n", MAXTUNNEL);
+       buf[0] = MAXTUNNEL;
+       buf[1] = sizeof(tunnelt);
+       if (fwrite(buf, sizeof(buf), 1, f) != 1) break;
+       if (fwrite(tunnel, sizeof(tunnelt), MAXTUNNEL, f) != MAXTUNNEL) break;
+
+       log(2, 0, 0, 0, "Dumping %u sessions\n", MAXSESSION);
+       buf[0] = MAXSESSION;
+       buf[1] = sizeof(sessiont);
+       if (fwrite(buf, sizeof(buf), 1, f) != 1) break;
+       if (fwrite(session, sizeof(sessiont), MAXSESSION, f) != MAXSESSION) break;
+
+       if (fclose(f) == 0) return; // OK
+    } while (0);
+
+    log(0, 0, 0, 0, "Can't write state information: %s\n", strerror(errno));
+    unlink(STATEFILE);
 }
 
 void build_chap_response(char *challenge, u8 id, u16 challenge_length, char **challenge_response)
@@ -2499,222 +2548,130 @@ void build_chap_response(char *challenge, u8 id, u16 challenge_length, char **ch
        MD5_CTX ctx;
        *challenge_response = NULL;
 
-       if (!l2tpsecret || !*l2tpsecret)
+       if (!*config->l2tpsecret)
        {
                log(0, 0, 0, 0, "LNS requested CHAP authentication, but no l2tp secret is defined\n");
                return;
        }
 
-       /*
-       if (challenge_length != 16)
-       {
-               log(0, 0, 0, 0, "Challenge length != 16.\n");
-               return;
-       }
-       */
-
        log(4, 0, 0, 0, "   Building challenge response for CHAP request\n");
 
        *challenge_response = (char *)calloc(17, 1);
 
        MD5Init(&ctx);
        MD5Update(&ctx, &id, 1);
-       MD5Update(&ctx, l2tpsecret, strlen(l2tpsecret));
+       MD5Update(&ctx, config->l2tpsecret, strlen(config->l2tpsecret));
        MD5Update(&ctx, challenge, challenge_length);
        MD5Final(*challenge_response, &ctx);
 
        return;
 }
 
-void read_config_file()
+static int facility_value(char *name)
 {
-       FILE *f;
-       char *buf;
-
-       if (!config_file) return;
-       if (!(f = fopen(config_file, "r"))) {
-               fprintf(stderr, "Can't open config file %s: %s\n", config_file, strerror(errno));
-               return;
-       }
+    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;
+}
 
-       if (radiussecret)
-       {
-               free(radiussecret);
-               radiussecret = NULL;
-       }
+void update_config()
+{
+       int i;
 
-       if (l2tpsecret)
-       {
-               free(l2tpsecret);
-               l2tpsecret = NULL;
-       }
+       snoop_addr.sin_family = AF_INET;
+       snoop_addr.sin_addr.s_addr = config->snoop_destination_host;
+       snoop_addr.sin_port = htons(config->snoop_destination_port);
 
-       if (log_filename)
+       // Update logging
+       closelog();
+       syslog_log = 0;
+       if (log_stream)
        {
-               free(log_filename);
-               log_filename = NULL;
+               fclose(log_stream);
+               log_stream = NULL;
        }
-
-       if (snoop_destination_host)
+       if (*config->log_filename)
        {
-               free(snoop_destination_host);
-               snoop_destination_host = NULL;
-       }
-
-       if (numradiusservers) {
-               int n;
-               for (n = 0; n < numradiusservers; n++)
-                       radiusserver[n] = 0;
-               numradiusservers = 0;
-       }
-
-       snoop_destination_port = 0L;
-       config_save_state = 0;
-       rl_rate = 0L;
-       debug = 1;
-       default_dns1 = default_dns2 = 0;
-       radius_accounting = 0;
-
-       buf = (char *)malloc(4096);
-
-       while (fgets(buf, 4096, f)) {
-               char *p, *t;
-
-               if (*buf == '#') continue;
-               if ((p = strchr(buf, '\n'))) *p = 0;
-               p = t = strchr(buf, '=');
-               if (!p) continue;
-               *p = 0; p++;
-               t--;
-               while (*p && *p == ' ') p++;
-               while (*t && *t == ' ') *t-- = 0;
-
-               if (strcmp(buf, "log file") == 0) {
-                       if (!log_filename)
-                               log_filename = strdup(p);
-               } else if (strcmp(buf, "l2tp secret") == 0) {
-                       if (!l2tpsecret)
-                               l2tpsecret = strdup(p);
-                       log(0, 0, 0, 0, "L2TP Secret is \"%s\"\n", l2tpsecret);
-               } else if (strcmp(buf, "radius secret") == 0) {
-                       if (!radiussecret)
-                               radiussecret = strdup(p);
-                       log(4, 0, 0, 0, "Radius Secret is \"%s\"\n", radiussecret);
-               } else if (strcmp(buf, "radius accounting") == 0) {
-                       radius_accounting = atoi(p);
-                       log(4, 0, 0, 0, "Radius Account is %s\n", radius_accounting ? "on" : "off");
-               } else if (strcmp(buf, "throttle rate") == 0) {
-                       rl_rate = atol(p);
-                       if (rl_rate == 0)
+               if (strstr(config->log_filename, "file:") == config->log_filename)
+               {
+                       if ((log_stream = fopen((char *)(config->log_filename + 5), "a")))
                        {
-                               log(1, 0, 0, 0, "Disabled throttling.\n");
+                               fseek(log_stream, 0, SEEK_END);
+                               setbuf(log_stream, NULL);
                        }
                        else
                        {
-                               log(1, 0, 0, 0, "Enabled throttling (rate is %lu kbits/s)\n", rl_rate);
+                               log_stream = stderr;
+                               setbuf(log_stream, NULL);
                        }
-               } else if (strcmp(buf, "debug") == 0) {
-                       debug = atoi(p);
-                       log(debug, 0, 0, 0, "Set debugging level to %d\n", debug);
-               } else if (strcmp(buf, "accounting dir") == 0) {
-                       accounting_dir = strdup(p);
-                       log(debug, 0, 0, 0, "Will dump accounting information to %s\n", accounting_dir);
-               } else if (strcmp(buf, "dns server") == 0) {
-                       unsigned long addr = 0;
-                       if (inet_aton(p, (struct in_addr *)&addr) < 0) {
-                               printf("Invalid DNS server %s\n", p);
-                               continue;
-                       }
-                       if (default_dns1 == 0)
-                               default_dns1 = addr;
-                       else if (default_dns2 == 0)
-                               default_dns2 = addr;
-               } else if (strcmp(buf, "radius server") == 0) {
-                       struct hostent *h = gethostbyname(p);
-                       if (h)
-                       {
-                               while (*h->h_addr_list)
-                               {
-                                       ipt ip = ntohl(*(u32 *) * h->h_addr_list);
-                                       if (numradiusservers < MAXRADSERVER)
-                                               radiusserver[numradiusservers++] = ip;
-                                       else
-                                               log(0, 0, 0, 0, "Too many RADIUS IPs\n");
-                                       h->h_addr_list++;
-                               }
-                       }
-                       else
-                       {        // may be IP?
-                               ipt ip = ntohl(inet_addr(p));
-                               if (ip && ip != 0xFFFFFFFF)
-                               {
-                                       if (numradiusservers < MAXRADSERVER)
-                                               radiusserver[numradiusservers++] = ip;
-                                       else
-                                               log(0, 0, 0, 0, "Too many RADIUS IPs\n");
-                               }
-                               else
-                                       log(0, 0, 0, 0, "Unknown server %s\n", p);
-                       }
-               } else if (strcmp(buf, "snoop host") == 0) {
-                       snoop_destination_host = strdup(p);
-               } else if (strcmp(buf, "snoop port") == 0) {
-                       snoop_destination_port = atol(p);
-               } else if (strcmp(buf, "bind address") == 0) {
-                       if (!bind_address)
-                       {
-                               // Already overridden on the command line
-                               bind_address = inet_addr(p);
-                               handle_interface = 1;
-                       }
-               } else if (strcmp(buf, "dump speed") == 0) {
-                       dump_speed = atoi(p);
-               } else if (strcmp(buf, "setuid") == 0) {
-                       target_uid = atoi(p);
-               } else if (strcmp(buf, "cluster master") == 0) {
-                       struct hostent *h = gethostbyname(p);
-                       if (h)
+               }
+               else if (strstr(config->log_filename, "syslog:") == config->log_filename)
+               {
+                       char *p = config->log_filename + 7;
+                       if (*p)
                        {
-                               if (*h->h_addr_list)
-                               {
-                                       cluster_address = *(u32 *) *h->h_addr_list;
-                               }
-                       }
-                       else
-                       {        // may be IP?
-                               cluster_address = inet_addr(p);
-                       }
-               } else if (strcmp(buf, "save state") == 0) {
-                       if (strcasecmp(p, "no") == 0) {
-                               config_save_state = 0;
-                       } else {
-                               config_save_state = 1;
+                               openlog("l2tpns", LOG_PID, facility_value(p));
+                               syslog_log = 1;
                        }
-               } else if (strcmp(buf, "plugin") == 0) {
-                       add_plugin(p);
-               } else {
-                       struct param_config cp = { buf, p };
-                       int rc = run_plugins(PLUGIN_CONFIG, &cp);
-                       if (rc == 0) log(0, 0, 0, 0, "Unknown config directive \"%s\"\n", buf);
                }
        }
+       else
+       {
+               log_stream = stderr;
+               setbuf(log_stream, NULL);
+       }
+
 
-       if (snoop_destination_host)
+       // Update radius
+       config->numradiusservers = 0;
+       for (i = 0; i < MAXRADSERVER; i++)
+               if (config->radiusserver[i]) config->numradiusservers++;
+
+       if (!config->numradiusservers)
        {
-               if (inet_aton(snoop_destination_host, &snoop_addr.sin_addr))
+               log(0, 0, 0, 0, "No RADIUS servers defined!\n");
+       }
+
+       // Update plugins
+       for (i = 0; i < MAXPLUGINS; i++)
+       {
+               if (strcmp(config->plugins[i], config->old_plugins[i]) == 0)
+                       continue;
+               if (*config->plugins[i])
                {
-                       snoop_addr.sin_port = htons(snoop_destination_port);
-                       snoop_addr.sin_family = AF_INET;
+                       // Plugin added
+                       add_plugin(config->plugins[i]);
                }
-               else
+               else if (*config->old_plugins[i])
                {
-                       log(0, 0, 0, 0, "Can't find address for snoop host %s\n", snoop_destination_host);
+                       // Plugin removed
+                       remove_plugin(config->old_plugins[i]);
                }
        }
+       memcpy(config->old_plugins, config->plugins, sizeof(config->plugins));
+       config->reload_config = 0;
+}
 
-       free(buf);
+void read_config_file()
+{
+       FILE *f;
+
+       if (!config->config_file) return;
+       if (!(f = fopen(config->config_file, "r"))) {
+               fprintf(stderr, "Can't open config file %s: %s\n", config->config_file, strerror(errno));
+               return;
+       }
+
+       log(3, 0, 0, 0, "Reading config file %s\n", config->config_file);
+       cli_do_file(f);
+       log(3, 0, 0, 0, "Done reading config file\n");
        fclose(f);
-       log(2, 0, 0, 0, "Done reading config file\n");
+       update_config();
+       log_stream = NULL;
 }
 
 int sessionsetup(tunnelidt t, sessionidt s, u8 routes)
@@ -2731,26 +2688,19 @@ int sessionsetup(tunnelidt t, sessionidt s, u8 routes)
                log(0, session[s].ip, s, t, "VERY VERY BAD! sessionsetup() called with no session[s].ip\n");
                return 1;
        }
-       if (session[s].ip == 0xFFFFFFFE)
-       {
-               session[s].ip = assign_ip_address(); // Assign one from the pool;
-
-               log(2, session[s].ip, s, t, "IP assigned is a magic token. Assign address from pool: %s\n",
-                               inet_toa(htonl(session[s].ip)));
-       }
 
        // Make sure this is right
        session[s].tunnel = t;
        // zap old sessions with same IP and/or username
-       // Don't kill walled_garden sessions - doing so leads to a DoS
+       // Don't kill walled garden sessions - doing so leads to a DoS
        // from someone who doesn't need to know the password
        ip = session[s].ip;
        user = session[s].user;
-       for (i = 0; i < MAXSESSION; i++)
+       for (i = 1; i < MAXSESSION; i++)
        {
                if (i == s) continue;
                if (ip == session[i].ip) sessionkill(i, "Duplicate IP address");
-               if (!session[s].walled_garden && !session[i].walled_garden && strcasecmp(user, session[i].user) == 0)
+               if (!session[s].servicenet && !session[i].servicenet && strcasecmp(user, session[i].user) == 0)
                        sessionkill(i, "Duplicate session for user");
        }
 
@@ -2779,7 +2729,9 @@ int sessionsetup(tunnelidt t, sessionidt s, u8 routes)
                run_plugins(PLUGIN_NEW_SESSION, &data);
        }
 
-       session[s].sid = ++last_sid;
+       if (!session[s].sid)
+               session[s].sid = ++last_sid;
+
        cache_sessionid(htonl(session[s].ip), s);
 
        cluster_send_session(s);
@@ -2951,7 +2903,8 @@ void processcontrol(u8 * buf, int len, struct sockaddr_in *addr)
        struct param_control param = { buf, len, ntohl(addr->sin_addr.s_addr), ntohs(addr->sin_port), NULL, 0, 0 };
 
        log(4, ntohl(addr->sin_addr.s_addr), 0, 0, "Received ");
-       dump_packet(buf, log_stream);
+       if (log_stream)
+               dump_packet(buf, log_stream);
 
        resp = calloc(1400, 1);
        l = new_packet(PKT_RESP_ERROR, resp);
@@ -2986,3 +2939,46 @@ void processcontrol(u8 * buf, int len, struct sockaddr_in *addr)
        free(resp);
 }
 
+/*
+ * HACK
+ * Go through all of the tunnels and do some cleanups
+ */
+void tunnel_clean()
+{
+       int i;
+
+       log(1, 0, 0, 0, "Cleaning tunnels array\n");
+
+       for (i = 1; i < MAXTUNNEL; i++)
+       {
+               if (!tunnel[i].ip
+                               || !*tunnel[i].hostname
+                               || (tunnel[i].state == TUNNELDIE && tunnel[i].die >= time_now))
+               {
+                       tunnelclear(i);
+               }
+       }
+}
+
+void tunnelclear(tunnelidt t)
+{
+       if (!t) return;
+       memset(&tunnel[t], 0, sizeof(tunnel[t]));
+       tunnel[t].state = TUNNELFREE;
+}
+
+tunnelidt new_tunnel()
+{
+       tunnelidt i;
+       for (i = 1; i < MAXTUNNEL; i++)
+       {
+               if (tunnel[i].state == TUNNELFREE)
+               {
+                       log(4, 0, 0, i, "Assigning tunnel ID %d\n", i);
+                       return i;
+               }
+       }
+       log(0, 0, 0, 0, "Can't find a free tunnel! There shouldn't be this many in use!\n");
+       return 0;
+}
+
index e9c5907..f591178 100644 (file)
--- a/l2tpns.h
+++ b/l2tpns.h
@@ -1,12 +1,11 @@
 // L2TPNS Global Stuff
-// $Id: l2tpns.h,v 1.1 2003/12/16 07:07:39 fred_nerk Exp $
+// $Id: l2tpns.h,v 1.2 2004/03/05 00:09:03 fred_nerk Exp $
 
 #include <netinet/in.h>
 #include <stdio.h>
-
 #include "config.h"
 
-#define VERSION        "1.0"
+#define VERSION        "1.1.0"
 
 // Limits
 #define MAXTUNNEL      500             // could be up to 65535
@@ -15,6 +14,7 @@
 #define        MAXCONTROL      1000            // max length control message we ever send...
 #define        MAXETHER        (1500+18)       // max packet we try sending to tap
 #define        MAXTEL          96              // telephone number
+#define MAXPLUGINS     20              // maximum number of plugins to load
 #define MAXRADSERVER   10              // max radius servers
 #define        MAXROUTE        10              // max static routes per session
 #define MAXIPPOOL      131072          // max number of ip addresses in pool
 #define STATISTICS
 #define STAT_CALLS
 #define RINGBUFFER
-#define        UDP             17
 #define TAPDEVICE      "/dev/net/tun"
-#define CLIUSERS       ETCDIR "l2tpns.users"   // CLI Users file
+#define        UDP             17
+#define HOMEDIR                "/home/l2tpns/"                 // Base dir for data
+#define STATEFILE      "/tmp/l2tpns.dump"              // State dump file
+#define NOSTATEFILE    "/tmp/l2tpns.no_state_reload"   // If exists, state will not be reloaded
 #define CONFIGFILE     ETCDIR "l2tpns.cfg"     // Configuration file
-#define IPPOOLFILE     ETCDIR "l2tpns.ip_pool" // Address pool configuration
-#define STATEFILE      "/tmp/l2tpns.dump"      // State dump file
-
+#define CLIUSERS       ETCDIR "l2tpns.users"           // CLI Users file
+#define IPPOOLFILE     ETCDIR "l2tpns.ip_pool"         // Address pool configuration
 #ifndef LIBDIR
 #define LIBDIR         "/usr/lib/l2tpns"
 #endif
-
 #define ACCT_TIME      3000            // 5 minute accounting interval
 #define        L2TPPORT        1701            // L2TP port
 #define RADPORT                1645            // old radius port...
 #define        PPPCCP          0x80FD
 #define PPPIP          0x0021
 #define PPPMP          0x003D
-#define ConfigReq      1
-#define ConfigAck      2
-#define ConfigNak      3
-#define ConfigRej      4
-#define TerminateReq   5
-#define TerminateAck   6
-#define CodeRej                7
-#define ProtocolRej    8
-#define EchoReq                9
-#define EchoReply      10
-#define DiscardRequest 11
-
-#undef TC_TBF
-#define TC_HTB
+enum
+{
+       ConfigReq = 1,
+       ConfigAck,
+       ConfigNak,
+       ConfigRej,
+       TerminateReq,
+       TerminateAck,
+       CodeRej,
+       ProtocolRej,
+       EchoReq,
+       EchoReply,
+       DiscardRequest
+};
 
 // Types
 typedef unsigned short u16;
@@ -78,6 +78,9 @@ typedef u16 tunnelidt;
 typedef u32 clockt;
 typedef u8 hasht[16];
 
+// dump header: update number if internal format changes
+#define DUMP_MAGIC "L2TPNS#" VERSION "#"
+
 // structures
 typedef struct routes           // route
 {
@@ -88,7 +91,7 @@ routet;
 
 typedef struct controls         // control message
 {
-       struct controls *next;       // next in queue
+       struct controls *next;  // next in queue
        u16 length;             // length
        u8 buf[MAXCONTROL];
 }
@@ -106,34 +109,37 @@ typedef struct stbft
 // 336 bytes per session
 typedef struct sessions
 {
-       sessionidt next;             // next session in linked list
-       sessionidt far;              // far end session ID
-       tunnelidt tunnel;            // tunnel ID
-       ipt ip;                 // IP of session set by RADIUS response
-       unsigned long sid;      // session id for hsddb
-       u16 nr;                 // next receive
-       u16 ns;                 // next send
-       u32 magic;              // ppp magic number
-       u32 cin, cout;               // byte counts
-       u32 pin, pout;               // packet counts
-       u32 id;                 // session id
-       clockt opened;             // when started
-       clockt die;                // being closed, when to finally free
-       time_t last_packet;     // Last packet from the user (used for idle timeouts)
-       ipt dns1, dns2;               // DNS servers
-       routet route[MAXROUTE];    // static routes
-       u8 radius;             // which radius session is being used (0 for not waiting on authentication)
-       u8 flags;              // various bit flags
-       u8 snoop;               // are we snooping this session?
-       u8 throttle;            // is this session throttled?
-       u8 walled_garden;       // is this session stuck in the walled garden?
-       u16 mru;                // maximum receive unit
-       u16 tbf;                // filter bucket for throttling
+       sessionidt next;                // next session in linked list
+       sessionidt far;                 // far end session ID
+       tunnelidt tunnel;               // tunnel ID
+       ipt ip;                         // IP of session set by RADIUS response
+       int ip_pool_index;              // index to IP pool
+       unsigned long sid;              // session id for hsddb
+       u16 nr;                         // next receive
+       u16 ns;                         // next send
+       u32 magic;                      // ppp magic number
+       u32 cin, cout;                  // byte counts
+       u32 pin, pout;                  // packet counts
+       u32 total_cin;                  // This counter is never reset while a session is open
+       u32 total_cout;                 // This counter is never reset while a session is open
+       u32 id;                         // session id
+       clockt opened;                  // when started
+       clockt die;                     // being closed, when to finally free
+       time_t last_packet;             // Last packet from the user (used for idle timeouts)
+       ipt dns1, dns2;                 // DNS servers
+       routet route[MAXROUTE];         // static routes
+       u8 radius;                      // which radius session is being used (0 for not waiting on authentication)
+       u8 flags;                       // various bit flags
+       u8 snoop;                       // are we snooping this session?
+       u8 throttle;                    // is this session throttled?
+       u8 servicenet;                  // is this session servicenetted?
+       u16 mru;                        // maximum receive unit
+       u16 tbf;                        // filter bucket for throttling
        char random_vector[MAXTEL];
        int random_vector_length;
-       char user[129];          // user (needed in seesion for radius stop messages)
-       char called[MAXTEL];     // called number
-       char calling[MAXTEL];     // calling number
+       char user[129];                 // user (needed in seesion for radius stop messages)
+       char called[MAXTEL];            // called number
+       char calling[MAXTEL];           // calling number
        unsigned long tx_connect_speed;
        unsigned long rx_connect_speed;
 }
@@ -145,16 +151,17 @@ sessiont;
 // 168 bytes per tunnel
 typedef struct tunnels
 {
-       tunnelidt next;         // next tunnel in linked list
        tunnelidt far;          // far end tunnel ID
        ipt ip;                 // Ip for far end
        portt port;             // port for far end
        u16 window;             // Rx window
        u16 nr;                 // next receive
        u16 ns;                 // next send
+       int state;              // current state (tunnelstate enum)
        clockt last;            // when last control message sent (used for resend timeout)
        clockt retry;           // when to try resenting pending control
        clockt die;             // being closed, when to finally free
+       clockt lastrec;         // when the last control message was received
        char hostname[128];     // tunnel hostname
        char vendor[128];       // LAC vendor
        u8 try;                 // number of retrys on a control message
@@ -167,10 +174,9 @@ tunnelt;
 // 180 bytes per radius session
 typedef struct radiuss          // outstanding RADIUS requests
 {
-       u8 next;               // next in free list
        sessionidt session;          // which session this applies to
        hasht auth;               // request authenticator
-       clockt retry;              // ehwne to try next
+       clockt retry;              // when to try next
        char calling[MAXTEL];    // calling number
        char pass[129];          // password
        u8 id;                 // ID for PPP response
@@ -184,6 +190,8 @@ typedef struct
 {
        ipt     address;
        char    assigned;       // 1 if assigned, 0 if free
+       clockt  last;           // last used
+       char    user[129];      // user (try to have ip addresses persistent)
 }
 ippoolt;
 
@@ -202,15 +210,27 @@ struct Tringbuffer
 };
 #endif
 
+/*
+ * Possible tunnel states
+ * TUNNELFREE -> TUNNELOPEN -> TUNNELDIE -> TUNNELFREE
+ */
 enum
 {
-    RADIUSNULL,                   // Not in use
-    RADIUSCHAP,                   // sending CHAP down PPP
-    RADIUSAUTH,                   // sending auth to RADIUS server
-    RADIUSIPCP,                   // sending IPCP to end user
-    RADIUSSTART,                  // sending start accounting to RADIUS server
-    RADIUSSTOP,                   // sending stop accounting to RADIUS server
-    RADIUSWAIT                   // waiting timeout before available, in case delayed replies
+       TUNNELFREE,             // Not in use
+       TUNNELOPEN,             // Active tunnel
+       TUNNELDIE,              // Currently closing
+       TUNNELOPENING           // Busy opening
+};
+
+enum
+{
+       RADIUSNULL,             // Not in use
+       RADIUSCHAP,             // sending CHAP down PPP
+       RADIUSAUTH,             // sending auth to RADIUS server
+       RADIUSIPCP,             // sending IPCP to end user
+       RADIUSSTART,            // sending start accounting to RADIUS server
+       RADIUSSTOP,             // sending stop accounting to RADIUS server
+       RADIUSWAIT              // waiting timeout before available, in case delayed replies
 };
 
 struct Tstats
@@ -297,6 +317,49 @@ struct Tstats
 #define SET_STAT(x, y)
 #endif
 
+struct configt
+{
+       int             debug;                          // debugging level
+       time_t          start_time;                     // time when l2tpns was started
+       char            bandwidth[256];                 // current bandwidth
+
+       char            config_file[128];
+       int             reload_config;                  // flag to re-read config (set by cli)
+
+       char            tapdevice[10];                  // tap device name
+       char            log_filename[128];
+       char            l2tpsecret[64];
+
+       char            radiussecret[64];
+       int             radius_accounting;
+       ipt             radiusserver[MAXRADSERVER];     // radius servers
+       u8              numradiusservers;               // radius server count
+
+       ipt             default_dns1, default_dns2;
+
+       ipt             snoop_destination_host;
+       u16             snoop_destination_port;
+
+       unsigned long   rl_rate;
+       int             save_state;
+       uint32_t        cluster_address;
+       int             ignore_cluster_updates;
+       char            accounting_dir[128];
+       ipt             bind_address;
+       int             target_uid;
+       int             dump_speed;
+       char            plugins[64][MAXPLUGINS];
+       char            old_plugins[64][MAXPLUGINS];
+};
+
+struct config_descriptt
+{
+       char *key;
+       int offset;
+       int size;
+       enum { INT, STRING, UNSIGNED_LONG, SHORT, BOOL, IP } type;
+};
+
 // arp.c
 void sendarp(int ifr_idx, const unsigned char* mac, ipt ip);
 
@@ -321,6 +384,7 @@ void radiussend(u8 r, u8 state);
 void processrad(u8 *buf, int len);
 void radiusretry(u8 r);
 u8 radiusnew(sessionidt s);
+void radiusclear(u8 r, sessionidt s);
 
 // throttle.c
 int throttle_session(sessionidt s, int throttle);
@@ -343,7 +407,6 @@ void initudp(void);
 void initdata(void);
 void initippool();
 sessionidt sessionbyip(ipt ip);
-/* NB - sessionbyuser ignores walled garden'd sessions */
 sessionidt sessionbyuser(char *username);
 void sessionshutdown(sessionidt s, char *reason);
 void sessionsendarp(sessionidt s);
@@ -365,14 +428,14 @@ void processarp(u8 * buf, int len);
 void processudp(u8 * buf, int len, struct sockaddr_in *addr);
 void processtap(u8 * buf, int len);
 void processcontrol(u8 * buf, int len, struct sockaddr_in *addr);
-ipt assign_ip_address();
-void free_ip_address(ipt address);
+int assign_ip_address(sessionidt s);
+void free_ip_address(sessionidt s);
 void snoop_send_packet(char *packet, u16 size);
 void dump_acct_info();
 void mainloop(void);
 #define log _log
 #ifndef log_hex
-#define log_hex(a,b,c,d) do{if (a <= debug) _log_hex(a,0,0,0,b,c,d);}while (0)
+#define log_hex(a,b,c,d) do{if (a <= config->debug) _log_hex(a,0,0,0,b,c,d);}while (0)
 #endif
 void _log(int level, ipt address, sessionidt s, tunnelidt t, const char *format, ...);
 void _log_hex(int level, ipt address, sessionidt s, tunnelidt t, const char *title, const char *data, int maxsize);
@@ -380,10 +443,10 @@ void build_chap_response(char *challenge, u8 id, u16 challenge_length, char **ch
 int sessionsetup(tunnelidt t, sessionidt s, u8 routes);
 int cluster_send_session(int s);
 int cluster_send_tunnel(int t);
-#ifdef HAVE_LIBCLI
+int cluster_send_goodbye();
 void init_cli();
+void cli_do_file(FILE *fh);
 void cli_do(int sockfd);
-#endif
 #ifdef RINGBUFFER
 void ringbuffer_dump(FILE *stream);
 #endif
@@ -391,3 +454,9 @@ void initplugins();
 int run_plugins(int plugin_type, void *data);
 void add_plugin(char *plugin_name);
 void remove_plugin(char *plugin_name);
+void tunnelclear(tunnelidt t);
+void host_unreachable(ipt destination, u16 id, ipt source, char *packet, int packet_len);
+
+extern tunnelt *tunnel;
+extern sessiont *session;
+#define sessionfree (session[0].next)
diff --git a/ll.c b/ll.c
index 7dd67e9..1b5a8ac 100644 (file)
--- a/ll.c
+++ b/ll.c
@@ -1,5 +1,5 @@
 // L2TPNS Linked List Stuff
-// $Id: ll.c,v 1.1 2003/12/16 07:07:39 fred_nerk Exp $
+// $Id: ll.c,v 1.2 2004/03/05 00:09:03 fred_nerk Exp $
 
 #include <stdio.h>
 #include <sys/file.h>
diff --git a/md5.h b/md5.h
index 53ecf51..cf6eee8 100644 (file)
--- a/md5.h
+++ b/md5.h
@@ -1,8 +1,6 @@
 /* GLOBAL.H - RSAREF types and constants
  */
 
-#include "config.h"
-
 /* PROTOTYPES should be set to one if and only if the compiler supports
   function argument prototyping.
 The following makes PROTOTYPES default to 0 if it has not already
diff --git a/nsctl.c b/nsctl.c
index 8a8aee0..aa6a300 100644 (file)
--- a/nsctl.c
+++ b/nsctl.c
@@ -96,7 +96,7 @@ int main(int argc, char *argv[])
                        }
                        for (p = 0; p < commands[i].params; p++)
                        {
-                               strncpy((packet + len), argv[p + 3], 1400 - len);
+                               strncpy((packet + len), argv[p + 3], 1400 - len - 1);
                                len += strlen(argv[p + 3]) + 1;
                        }
                        break;
index 28855e1..b41b16f 100644 (file)
--- a/plugin.h
+++ b/plugin.h
@@ -4,16 +4,18 @@
 #define PLUGIN_API_VERSION     1
 #define MAX_PLUGIN_TYPES       30
 
-#define PLUGIN_PRE_AUTH                1
-#define PLUGIN_POST_AUTH       2
-#define PLUGIN_PACKET_RX       3
-#define PLUGIN_PACKET_TX       4
-#define PLUGIN_TIMER           5
-#define PLUGIN_CONFIG          6
-#define PLUGIN_NEW_SESSION     7
-#define PLUGIN_KILL_SESSION    8
-#define PLUGIN_CONTROL         9
-#define PLUGIN_RADIUS_RESPONSE 10
+enum
+{
+       PLUGIN_PRE_AUTH = 1,
+       PLUGIN_POST_AUTH,
+       PLUGIN_PACKET_RX,
+       PLUGIN_PACKET_TX,
+       PLUGIN_TIMER,
+       PLUGIN_NEW_SESSION,
+       PLUGIN_KILL_SESSION,
+       PLUGIN_CONTROL,
+       PLUGIN_RADIUS_RESPONSE
+};
 
 #define PLUGIN_RET_ERROR       0
 #define PLUGIN_RET_OK          1
diff --git a/ppp.c b/ppp.c
index 3cc73c2..901f8e5 100644 (file)
--- a/ppp.c
+++ b/ppp.c
@@ -1,5 +1,5 @@
 // L2TPNS PPP Stuff
-// $Id: ppp.c,v 1.1 2003/12/16 07:07:39 fred_nerk Exp $
+// $Id: ppp.c,v 1.2 2004/03/05 00:09:03 fred_nerk Exp $
 
 #include <stdio.h>
 #include <string.h>
 #include "plugin.h"
 #include "util.h"
 
-extern char debug;
 extern tunnelt *tunnel;
 extern sessiont *session;
 extern radiust *radius;
-extern u16 tapmac[3];
 extern int tapfd;
 extern char hostname[1000];
 extern struct Tstats *_statistics;
 extern unsigned long eth_tx;
 extern time_t time_now;
+extern struct configt *config;
 
 // Process PAP messages
 void processpap(tunnelidt t, sessionidt s, u8 * p, u16 l)
@@ -77,7 +76,7 @@ void processpap(tunnelidt t, sessionidt s, u8 * p, u16 l)
                p[4] = 0;               // no message
                if (session[s].ip)
                {
-                   log(3, session[s].ip, s, t, "%d Already an IP allocated: %s (%d)\n", getpid(), inet_toa(htonl(session[s].ip)), session[s].ip);
+                   log(3, session[s].ip, s, t, "%d Already an IP allocated: %s (%d)\n", getpid(), inet_toa(htonl(session[s].ip)), session[s].ip_pool_index);
                }
                else
                {
@@ -101,8 +100,8 @@ void processpap(tunnelidt t, sessionidt s, u8 * p, u16 l)
                        return;
                }
 
-               strncpy(session[s].user, packet.username, sizeof(session[s].user));
-               strncpy(radius[r].pass, packet.password, sizeof(radius[r].pass));
+               strncpy(session[s].user, packet.username, sizeof(session[s].user) - 1);
+               strncpy(radius[r].pass, packet.password, sizeof(radius[r].pass) - 1);
 
                free(packet.username);
                free(packet.password);
@@ -180,7 +179,7 @@ void processchap(tunnelidt t, sessionidt s, u8 * p, u16 l)
                        return;
                }
 
-               strncpy(session[s].user, packet.username, sizeof(session[s].user));
+               strncpy(session[s].user, packet.username, sizeof(session[s].user) - 1);
                memcpy(radius[r].pass, packet.password, 16);
 
                free(packet.username);
@@ -408,7 +407,7 @@ void processlcp(tunnelidt t, sessionidt s, u8 * p, u16 l)
                *p = EchoReply;      // reply
                *(u32 *) (p + 4) = htonl(session[s].magic); // our magic number
                q = makeppp(b, p, l, t, s, PPPLCP);
-               log(3, session[s].ip, s, t, "LCP: Received EchoReq. Sending EchoReply\n");
+               log(4, session[s].ip, s, t, "LCP: Received EchoReq. Sending EchoReply\n");
                tunnelsend(b, l + (q - b), t); // send it
        }
        else if (*p == EchoReply)
@@ -439,7 +438,7 @@ void processipcp(tunnelidt t, sessionidt s, u8 * p, u16 l)
        if (*p == ConfigAck)
        {                          // happy with our IPCP
                u8 r = session[s].radius;
-               if ((!r || radius[r].state == RADIUSIPCP) && !session[s].walled_garden)
+               if ((!r || radius[r].state == RADIUSIPCP) && !session[s].servicenet)
                        if (!r)
                                r = radiusnew(s);
                        if (r)
@@ -552,6 +551,7 @@ void processipin(tunnelidt t, sessionidt s, u8 * p, u16 l)
        }
 
        session[s].cin += l;
+       session[s].total_cin += l;
        session[s].pin++;
        eth_tx += l;
 
index ee364b2..c0ec9ab 100644 (file)
--- a/radius.c
+++ b/radius.c
@@ -1,5 +1,5 @@
 // L2TPNS Radius Stuff
-// $Id: radius.c,v 1.1 2003/12/16 07:07:39 fred_nerk Exp $
+// $Id: radius.c,v 1.2 2004/03/05 00:09:03 fred_nerk Exp $
 
 #include <time.h>
 #include <stdio.h>
 #include "plugin.h"
 #include "util.h"
 
-extern char *radiussecret;
 extern radiust *radius;
 extern sessiont *session;
 extern tunnelt *tunnel;
-extern ipt radiusserver[MAXRADSERVER]; // radius servers
 extern u32 sessionid;
-extern u8 radiusfree;
 extern int radfd;
-extern u8 numradiusservers;
-extern char debug;
-extern unsigned long default_dns1, default_dns2;
 extern struct Tstats *_statistics;
-extern int radius_accounting;
-extern uint32_t bind_address;
+extern struct configt *config;
 
 const char *radius_state(int state)
 {
@@ -51,27 +44,35 @@ void initrad(void)
 
 void radiusclear(u8 r, sessionidt s)
 {
-       radius[r].state = RADIUSNULL;
        if (s) session[s].radius = 0;
-       memset(&radius[r], 0, sizeof(radius[r]));
-       radius[r].next = radiusfree;
-       radiusfree = r;
+       memset(&radius[r], 0, sizeof(radius[r])); // radius[r].state = RADIUSNULL;
+}
+
+static u8 new_radius()
+{
+       u8 i;
+       for (i = 1; i < MAXRADIUS; i++)
+       {
+               if (radius[i].state == RADIUSNULL)
+                       return i;
+       }
+       log(0, 0, 0, 0, "Can't find a free radius session! This could be bad!\n");
+       return 0;
 }
 
 u8 radiusnew(sessionidt s)
 {
        u8 r;
-       if (!radiusfree)
+       if (!(r = new_radius()))
        {
                log(1, 0, s, session[s].tunnel, "No free RADIUS sessions\n");
                STAT(radius_overflow);
                return 0;
        };
-       r = radiusfree;
-       session[s].radius = r;
-       radiusfree = radius[r].next;
        memset(&radius[r], 0, sizeof(radius[r]));
+       session[s].radius = r;
        radius[r].session = s;
+       radius[r].state = RADIUSWAIT;
        return r;
 }
 
@@ -87,19 +88,19 @@ void radiussend(u8 r, u8 state)
 #ifdef STAT_CALLS
        STAT(call_radiussend);
 #endif
-       if (!numradiusservers)
+       s = radius[r].session;
+       if (!config->numradiusservers)
        {
-               log(0, 0, 0, 0, "No RADIUS servers\n");
+               log(0, 0, s, session[s].tunnel, "No RADIUS servers\n");
                return;
        }
-       if (!radiussecret)
+       if (!*config->radiussecret)
        {
-               log(0, 0, 0, 0, "No RADIUS secret\n");
+               log(0, 0, s, session[s].tunnel, "No RADIUS secret\n");
                return;
        }
-       s = radius[r].session;
 
-       if (state != RADIUSAUTH && !radius_accounting)
+       if (state != RADIUSAUTH && !config->radius_accounting)
        {
                // Radius accounting is turned off
                radiusclear(r, s);
@@ -111,7 +112,7 @@ void radiussend(u8 r, u8 state)
        radius[r].state = state;
        radius[r].retry = backoff(radius[r].try++);
        log(4, 0, s, session[s].tunnel, "Send RADIUS %d state %s try %d\n", r, radius_state(radius[r].state), radius[r].try);
-       if (radius[r].try > numradiusservers * 2)
+       if (radius[r].try > config->numradiusservers * 2)
        {
                if (s)
                {
@@ -177,7 +178,7 @@ void radiussend(u8 r, u8 state)
                                {
                                        MD5_CTX ctx;
                                        MD5Init(&ctx);
-                                       MD5Update(&ctx, radiussecret, strlen(radiussecret));
+                                       MD5Update(&ctx, config->radiussecret, strlen(config->radiussecret));
                                        if (p)
                                                MD5Update(&ctx, pass + p - 16, 16);
                                        else
@@ -280,7 +281,7 @@ void radiussend(u8 r, u8 state)
        // NAS-IP-Address
        *p = 4;
        p[1] = 6;
-       *(u32 *)(p + 2) = bind_address;
+       *(u32 *)(p + 2) = config->bind_address;
        p += p[1];
 
        // All AVpairs added
@@ -295,14 +296,14 @@ void radiussend(u8 r, u8 state)
            MD5Update(&ctx, b, 4);
            MD5Update(&ctx, z, 16);
            MD5Update(&ctx, b + 20, (p - b) - 20);
-           MD5Update(&ctx, radiussecret, strlen(radiussecret));
+           MD5Update(&ctx, config->radiussecret, strlen(config->radiussecret));
            MD5Final(hash, &ctx);
            memcpy(b + 4, hash, 16);
            memcpy(radius[r].auth, hash, 16);
        }
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
-       *(u32 *) & addr.sin_addr = htonl(radiusserver[(radius[r].try - 1) % numradiusservers]);
+       *(u32 *) & addr.sin_addr = config->radiusserver[(radius[r].try - 1) % config->numradiusservers];
        addr.sin_port = htons((state == RADIUSAUTH) ? RADPORT : RADAPORT);
 
        log_hex(5, "RADIUS Send", b, (p - b));
@@ -348,7 +349,7 @@ void processrad(u8 * buf, int len)
        MD5Update(&ctx, buf, 4);
        MD5Update(&ctx, radius[r].auth, 16);
        MD5Update(&ctx, buf + 20, len - 20);
-       MD5Update(&ctx, radiussecret, strlen(radiussecret));
+       MD5Update(&ctx, config->radiussecret, strlen(config->radiussecret));
        MD5Final(hash, &ctx);
        do {
                if (memcmp(hash, buf + 4, 16))
@@ -534,22 +535,22 @@ void processrad(u8 * buf, int len)
                        // Check for Assign-IP-Address
                        if (!session[s].ip || session[s].ip == 0xFFFFFFFE)
                        {
-                               session[s].ip = assign_ip_address();
+                               assign_ip_address(s);
                                if (session[s].ip)
                                        log(3, 0, s, t, "   No IP allocated by radius. Assigned %s from pool\n",
                                                        inet_toa(htonl(session[s].ip)));
                                else
                                        log(3, 0, s, t, "   No IP allocated by radius. None available in pool\n");
                        }
-                       if (!session[s].dns1 && default_dns1)
+                       if (!session[s].dns1 && config->default_dns1)
                        {
-                               session[s].dns1 = htonl(default_dns1);
-                               log(3, 0, s, t, "   Sending dns1 = %s\n", inet_toa(default_dns1));
+                               session[s].dns1 = htonl(config->default_dns1);
+                               log(3, 0, s, t, "   Sending dns1 = %s\n", inet_toa(config->default_dns1));
                        }
-                       if (!session[s].dns2 && default_dns2)
+                       if (!session[s].dns2 && config->default_dns2)
                        {
-                               session[s].dns2 = htonl(default_dns2);
-                               log(3, 0, s, t, "   Sending dns2 = %s\n", inet_toa(default_dns2));
+                               session[s].dns2 = htonl(config->default_dns2);
+                               log(3, 0, s, t, "   Sending dns2 = %s\n", inet_toa(config->default_dns2));
                        }
 
                        if (session[s].ip)
@@ -612,3 +613,18 @@ void radiusretry(u8 r)
        }
 }
 
+void radius_clean()
+{
+       int i;
+
+       log(1, 0, 0, 0, "Cleaning radius session array\n");
+
+       for (i = 1; i < MAXRADIUS; i++)
+       {
+               if (radius[i].retry == 0
+                               || !session[radius[i].session].opened
+                               || session[radius[i].session].die
+                               || session[radius[i].session].tunnel == 0)
+                       radiusclear(i, 0);
+       }
+}
diff --git a/rl.c b/rl.c
index ae0d33c..b0c01bc 100644 (file)
--- a/rl.c
+++ b/rl.c
@@ -1,5 +1,5 @@
 // L2TPNS Rate Limiting Stuff
-// $Id: rl.c,v 1.1 2003/12/16 07:07:39 fred_nerk Exp $
+// $Id: rl.c,v 1.2 2004/03/05 00:09:03 fred_nerk Exp $
 
 #include <stdio.h>
 #include <sys/file.h>
 #include <malloc.h>
 #include "l2tpns.h"
 
-extern char *radiussecret;
 extern radiust *radius;
 extern sessiont *session;
-extern ipt radiusserver[MAXRADSERVER]; // radius servers
 extern u32 sessionid;
-extern u8 radiusfree;
 extern int radfd;
-extern u8 numradiusservers;
-extern char debug;
-extern char *tapdevice;
 extern tbft *filter_buckets;
+extern struct configt *config;
 
 #define DEVICE "tun0"
 
-unsigned long rl_rate = 0;
 int next_tbf = 1;
 
 void init_rl()
 {
-#ifdef TC_TBF
-    system("tc qdisc del dev " DEVICE " root");
-    system("tc qdisc add dev " DEVICE " root handle 1: cbq avpkt 10000 bandwidth 100mbit");
-    system("tc filter del dev " DEVICE " protocol ip pref 1 fw");
-    system("iptables -t mangle -N throttle 2>&1 > /dev/null");
-    system("iptables -t mangle -F throttle");
-    system("iptables -t mangle -A l2tpns -j throttle");
-#endif
-#ifdef TC_HTB
     char *commands[] = {
        "tc qdisc add dev " DEVICE " root handle 1: htb default 1",
        "tc class add dev " DEVICE " parent 1: classid 1:1 htb rate 100mbit burst 300k",
        "tc filter del dev " DEVICE " protocol ip pref 1 fw",
-       "iptables -t mangle -N throttle 2>&1 > /dev/null",
-       "iptables -t mangle -F throttle",
-       "iptables -t mangle -A l2tpns -j throttle",
+       "iptables -t mangle -N throttle 2>&1 >/dev/null",
+       "iptables -t mangle -F throttle 2>&1 >/dev/null",
+       "iptables -t mangle -A l2tpns -j throttle 2>&1 >/dev/null",
        NULL
     };
     int i;
 
-    if (!rl_rate) return;
-
     log(2, 0, 0, 0, "Initializing HTB\n");
     for (i = 0; commands[i] && *commands[i]; i++)
     {
@@ -59,41 +42,21 @@ void init_rl()
        system(commands[i]);
     }
     log(2, 0, 0, 0, "Done initializing HTB\n");
-#endif
 }
 
 u16 rl_create_tbf()
 {
     u16 t;
     char cmd[2048];
-    if (!rl_rate) return 0;
+    if (!config->rl_rate) return 0;
 
     if (next_tbf >= MAXSESSION) return 0;
     t = next_tbf++;
     snprintf(filter_buckets[t].handle, 9, "1:%d0", t);
 
-#ifdef TC_TBF
-    log(2, 0, 0, 0, "Creating new tbf %s\n", filter_buckets[t].handle);
-    snprintf(cmd, 2048, "tc class add dev " DEVICE " parent 1: classid 1:%d cbq bandwidth 100Mbit rate 100Mbit "
-           "weight 1 prio 8 allot 1514 cell 8 maxburst 20 avpkt 1000 bounded isolated",
-           t);
-    log(3, 0, 0, 0, "%s\n", cmd);
-    system(cmd);
-
-    snprintf(cmd, 2048, "tc qdisc add dev " DEVICE " parent 1:%d handle %s tbf rate %dkbit buffer 1600 limit 3000",
-           t, filter_buckets[t].handle, rl_rate);
-    log(3, 0, 0, 0, "%s\n", cmd);
-    system(cmd);
-
-    snprintf(cmd, 2048, "tc filter add dev " DEVICE " protocol ip parent 1:0 prio 1 handle %d fw flowid 1:%d",
-           t, t);
-    log(3, 0, 0, 0, "%s\n", cmd);
-    system(cmd);
-#endif
-#ifdef TC_HTB
     log(2, 0, 0, 0, "Creating new htb %s\n", filter_buckets[t].handle);
     snprintf(cmd, 2048, "tc class add dev " DEVICE " parent 1: classid %s htb rate %lukbit burst 15k",
-           filter_buckets[t].handle, rl_rate);
+           filter_buckets[t].handle, config->rl_rate);
     log(3, 0, 0, 0, "%s\n", cmd);
     system(cmd);
 
@@ -101,7 +64,6 @@ u16 rl_create_tbf()
            t, filter_buckets[t].handle);
     log(3, 0, 0, 0, "%s\n", cmd);
     system(cmd);
-#endif
 
     next_tbf++;
     return t;
@@ -110,7 +72,7 @@ u16 rl_create_tbf()
 u16 rl_get_tbf()
 {
     int i;
-    if (!rl_rate) return 0;
+    if (!config->rl_rate) return 0;
 
     for (i = 1; i < MAXSESSION; i++)
     {
@@ -129,30 +91,24 @@ u16 rl_get_tbf()
 void rl_done_tbf(u16 t)
 {
     if (!t) return;
-    if (!rl_rate) return;
-    log(2, 0, 0, 0, "Freeing up TBF %s\n", filter_buckets[t].handle);
+    if (!config->rl_rate) return;
+    log(2, 0, 0, 0, "Freeing up HTB %s\n", filter_buckets[t].handle);
     filter_buckets[t].in_use = 0;
 }
 
 void rl_destroy_tbf(u16 t)
 {
     char cmd[2048];
-    if (!rl_rate) return;
+    if (!config->rl_rate) return;
     if (filter_buckets[t].in_use)
     {
-       log(0, 0, 0, 0, "Trying to destroy an in-use TBF %s\n", filter_buckets[t].handle);
+       log(0, 0, 0, 0, "Trying to destroy an in-use HTB %s\n", filter_buckets[t].handle);
        return;
     }
-#ifdef TC_TBF
-    snprintf(cmd, 2048, "tc qdisc del dev " DEVICE " handle %s", filter_buckets[t].handle);
-    system(cmd);
-#endif
-#ifdef TC_HTB
     snprintf(cmd, 2048, "tc qdisc del dev " DEVICE " handle %s", filter_buckets[t].handle);
     system(cmd);
-#endif
-    system("iptables -t mangle -D l2tpns -j throttle");
-    system("iptables -t mangle -X throttle");
+    system("iptables -t mangle -D l2tpns -j throttle 2>&1 >/dev/null");
+    system("iptables -t mangle -X throttle 2>&1 >/dev/null");
     memset(filter_buckets[t].handle, 0, sizeof(filter_buckets[t].handle));
 }
 
index c642d3c..7744204 100644 (file)
@@ -1,5 +1,5 @@
 // L2TPNS Throttle Stuff
-// $Id: throttle.c,v 1.1 2003/12/16 07:07:39 fred_nerk Exp $
+// $Id: throttle.c,v 1.2 2004/03/05 00:09:03 fred_nerk Exp $
 
 #include <stdio.h>
 #include <sys/file.h>
 #include "l2tpns.h"
 #include "util.h"
 
-extern char *radiussecret;
 extern radiust *radius;
 extern sessiont *session;
-extern ipt radiusserver[MAXRADSERVER]; // radius servers
 extern u32 sessionid;
-extern u8 radiusfree;
 extern int radfd;
-extern u8 numradiusservers;
-extern char debug;
-extern unsigned long rl_rate;
 extern tbft *filter_buckets;
+extern struct configt *config;
 
 // Throttle or Unthrottle a session
 int throttle_session(sessionidt s, int throttle)
 {
-       if (!rl_rate) return 0;
+       if (!config->rl_rate) return 0;
 
        if (!*session[s].user)
                return 0; // User not logged in
@@ -40,9 +35,15 @@ int throttle_session(sessionidt s, int throttle)
        {
                // Throttle them
                char cmd[2048] = {0};
-               log(2, 0, s, session[s].tunnel, "Throttling session %d for user %s\n", s, session[s].user);
                if (!session[s].tbf) session[s].tbf = rl_get_tbf();
-               snprintf(cmd, 2048, "iptables -t mangle -A throttle -d %s -j MARK --set-mark %d", inet_toa(ntohl(session[s].ip)),
+               if (!session[s].tbf)
+               {
+                       log(1, 0, s, session[s].tunnel, "Error creating a filtering bucket for user %s\n", session[s].user);
+                       return 0;
+               }
+               log(2, 0, s, session[s].tunnel, "Throttling session %d for user %s\n", s, session[s].user);
+               snprintf(cmd, 2048, "iptables -t mangle -A throttle -d %s -j MARK --set-mark %d",
+                               inet_toa(ntohl(session[s].ip)),
                                session[s].tbf);
                log(4, 0, s, session[s].tunnel, "Running %s\n", cmd);
                system(cmd);